Я пытаюсь перевести Arrows основной библиотеки Haskell на F # (я думаю, что это хорошее упражнение для лучшего понимания Arrows и F #, и я мог бы использовать их в проекте, над которым я работаю.) Однако прямой перевод невозможно из-за разницы в парадигмах. Haskell использует классы типов для выражения этого материала, но я не уверен, какие конструкции F# лучше всего отображают функциональность классов типов с идиомами F#. У меня есть несколько соображений, но я решил, что лучше всего поднять их здесь и посмотреть, что считается наиболее близким по функциональности.
Для толпы tl;dr: Как преобразовать классы типов (идиома Haskell) в идиоматический код F#?
Для тех, кто принимает мое длинное объяснение:
Этот код из стандартной библиотеки Haskell является примером того, что я пытаюсь перевести:
class Category cat where
id :: cat a a
comp :: cat a b -> cat b c -> cat a c
class Category a => Arrow a where
arr :: (b -> c) -> a b c
first :: a b c -> a (b,d) (c,d)
instance Category (->) where
id f = f
instance Arrow (->) where
arr f = f
first f = f *** id
Попытка 1: модули, простые типы, привязки Let
Моя первая попытка состояла в том, чтобы просто сопоставить вещи напрямую, используя модули для организации, например:
type Arrow<'a,'b> = Arrow of ('a -> 'b)
let arr f = Arrow f
let first f = //some code that does the first op
Это работает, но проигрывает полиморфизму, поскольку я не реализую категории и не могу легко реализовать более специализированные стрелки.
Попытка 1а: уточнение с помощью сигнатур и типов
Один из способов исправить некоторые проблемы с попыткой 1 — использовать файл .fsi для определения методов (чтобы упростить применение типов) и использовать некоторые простые настройки типов для специализации.
type ListArrow<'a,'b> = Arrow<['a],['b]>
//or
type ListArrow<'a,'b> = LA of Arrow<['a],['b]>
Но файл fsi нельзя повторно использовать (для обеспечения соблюдения типов связанных функций let) для других реализаций, а переименование/инкапсуляция типов сложны.
Попытка 2: Объектные модели и интерфейсы
Объясняя, что F# также создан для объектно-ориентированного программирования, возможно, иерархия типов — правильный способ сделать это.
type IArrow<'a,'b> =
abstract member comp : IArrow<'b,'c> -> IArrow<'a,'c>
type Arrow<'a,'b>(func:'a->'b) =
interface IArrow<'a,'b> with
member this.comp = //fun code involving "Arrow (fun x-> workOn x) :> IArrow"
Помимо того, насколько сложно заставить статические методы (такие как comp и другие операторы) работать как методы экземпляра, также необходимо явно повышать результаты. Я также не уверен, что эта методология все еще улавливает всю выразительность полиморфизма классов типов. Это также затрудняет использование вещей, которые ДОЛЖНЫ быть статическими методами.
Попытка 2а. Уточнение с помощью расширений типов
Таким образом, еще одно потенциальное усовершенствование состоит в том, чтобы объявить интерфейсы как можно более голыми, а затем использовать методы расширения для добавления функциональности ко всем реализующим типам.
type IArrow<'a,'b> with
static member (&&&) f = //code to do the fanout operation
Ах, но это заставляет меня использовать один метод для всех типов IArrow. Если бы я хотел немного другого (&&&) для ListArrows, что я мог бы сделать? Я еще не пробовал этот метод, но я думаю, что могу скрыть (&&&) или, по крайней мере, предоставить более специализированную версию, но я чувствую, что не могу принудительно использовать правильный вариант.
Помогите мне
Так что мне здесь делать? Я чувствую, что OO должен быть достаточно мощным, чтобы заменить классы типов, но я не могу понять, как это сделать в F #. Были ли какие-либо из моих попыток близкими? Являются ли какие-либо из них «настолько хорошими, насколько это возможно», и этого должно быть достаточно?