Я пытаюсь перевести базовую библиотеку Haskell Arrows в F # (я считаю это хорошим упражнением для понимания стрелок и 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: Модули, Простые типы, Ограничения привязки
Мой первый выстрел в этом состоял в том, чтобы просто сопоставить вещи непосредственно с помощью модулей для организации, например:
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 не может быть повторно использован (для принудительного применения типов связанных функций) для других реализаций, а материал переименования типа/инкапсуляции является сложным.
Попытка 2: Объектные модели и интерфейсы
Рационализация того, что F # также построена как OO, возможно, иерархия типов - это правильный способ сделать это.
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, что я могу сделать? Я еще не пробовал этот метод, но я бы предположил, что могу затенять (& & &) или, по крайней мере, предоставить более специализированную версию, но я чувствую, что не могу обеспечить использование правильного варианта,
Помогите мне
Так что я должен здесь делать? Я чувствую, что OO должно быть достаточно мощным, чтобы заменить классы типа, но я не могу понять, как это произошло в F #. Были ли какие-либо из моих попыток закрыты? Кто-нибудь из них "так хорош, как он", и это должно быть достаточно хорошим?