Я пытаюсь перевести стрелки базовой библиотеки 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 Bindings
Моей первой попыткой было просто сопоставить вещи напрямую, используя Модули для организации, например:
type Arrow<'a,'b> = Arrow of ('a -> 'b)
let arr f = Arrow f
let first f = //some code that does the first op
Это работает, но теряет полиморфизм , поскольку я не реализую категории и не могу легко реализовать более специализированные стрелки.
Попытка 1a: Уточнение с использованием сигнатур и типов
Один из способов исправить некоторые проблемы с Попыткой 1 - использовать файл .fsi для определения методов (чтобы типы выполнялись проще) и использовать некоторые простые настройки типов для специализации .
type ListArrow<'a,'b> = Arrow<['a],['b]>
//or
type ListArrow<'a,'b> = LA of Arrow<['a],['b]>
Но файл fsi может ' t могут быть повторно использованы (для принудительного применения типов связанных функций 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 и другие операторы), которые должны действовать как методы экземпляра, также необходимо чтобы явно улучшить результаты. Я также не уверен, что эта методология по-прежнему отражает всю выразительность полиморфизма классов типов. Это также затрудняет использование вещей, которые ДОЛЖНЫ быть статическими методами.
Попытка 2a: Уточнение с использованием расширений типов
Итак, еще одно возможное усовершенствование - объявить интерфейсы как можно более простыми, затем используйте методы расширения, чтобы добавить функциональность ко всем типам реализации.
type IArrow<'a,'b> with
static member (&&&) f = //code to do the fanout operation
Ах, но это заставляет меня использовать один метод для всех типов IArrow. Что я могу сделать, если мне нужен немного другой (&&&) для ListArrows? Я еще не пробовал этот метод, но полагаю, что могу затенять (&&&) или, по крайней мере, предоставить более специализированную версию, но мне кажется, что я не могу принудительно использовать правильный вариант.
Помогите мне
Итак, что мне здесь делать? Я чувствую, что объектно-ориентированный подход должен быть достаточно мощным, чтобы заменить классы типов, но я не могу понять, как это сделать в F #. Были ли какие-нибудь из моих попыток закрытыми? Кто-нибудь из них «настолько хорош, насколько это возможно», и этого должно быть достаточно?