2010-10-27 2 views
40

Я пытаюсь перевести стрелы активной библиотеки Haskell в F # (я думаю, что это хорошее упражнение для понимания стрелок и F # лучше, и я мог бы использовать их в проекте, над которым я работаю.) Однако, прямой перевод невозможен из-за различий в парадигмах. Haskell использует типы классов для выражения этого материала, но я не уверен, что конструкция F # наилучшим образом отображает функциональность классов типов с идиомами F #. У меня есть несколько мыслей, но я решил, что лучше всего поднять его здесь и посмотреть, что считается самым близким по функциональности.Как перевести класс типа Haskell в F #?

Для толпы tl; dr: Как перевести типы классов (икону Haskell) в Идиоматический код F #?

Для тех, принимая моего долгого объяснения:

Этот код из стандартного Lib 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: Модули, Простые типы , Пусть Наручники

Мой первый выстрел в это просто карта вещи более непосредственно с помощью модулей для организации, как:

Это работает, но оно теряет полиморфизм, поскольку я не реализую Категории и не могу легко реализовать более специализированные стрелки.

Покушения 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" 

Кроме того, сколько боли это может быть, чтобы получить то, что должно быть статическими методами (например, комп и другими операторы), чтобы действовать как методы экземпляра, есть также необходимость явно базовый типа результатов. Я также не уверен, что эта методология все еще захватывает полную выразительность полиморфизма типового класса. Это также затрудняет использование вещей, которые ДОЛЖНЫ быть статическими методами.

Покушение 2a: нефтепереработка с использованием расширений типа

Так что еще один потенциальное уточнением объявить интерфейсы, как босые, насколько это возможно, а затем использовать методы расширения для добавления функциональности для всех типов исполнителей.

type IArrow<'a,'b> with 
    static member (&&&) f = //code to do the fanout operation 

Ах, но это заставляет меня использовать один метод для всех типов IArrow.Если бы я хотел немного отличаться (& & &) для ListArrows, что мне делать? Я еще не пробовал этот метод, но я бы предположил, что могу тень (& & &), или, по крайней мере, предоставить более специализированную версию, но я чувствую, что не могу обеспечить использование правильного варианта.

Помоги мне

Так что я должен здесь делать? Я чувствую, что OO должно быть достаточно мощным, чтобы заменить классы типа, но я не могу понять, как это произошло в F #. Были ли какие-либо из моих попыток закрыты? Кто-нибудь из них «так хорош, как он», и это должно быть достаточно хорошо?

+5

«Как перевести типы классов (икону Haskell) в Идиоматический код F #?» Плохая идея. Вы должны посмотреть на * проблему *, которую вы пытались решить, вместо решения, которое использовало классы типов, и выяснить, как его решить, используя функцию, которую предоставляет F #. –

+16

@Jon Harrop В этом суть вопроса. Haskell решает десятки проблем с классами типа, и я хотел знать, какие альтернативы F # решали аналогичный * класс * проблем.Также порт Arrow не должен решать никаких проблем, его просто «я думал, что было бы интересно узнать больше о стрелках». – CodexArcanum

+1

Тогда я думаю, что было бы очень полезно, если бы вы могли объяснить некоторые проблемы, которые решали типы классов, и спросить людей, как они решат те же проблемы в F #. –

ответ

21

Вот такой подход я использую для имитации (из классов типов http://code.google.com/p/fsharp-typeclasses/).

В вашем случае для стрелы может быть что-то вроде этого:

let inline i2 (a:^a,b:^b ) =              
    ((^a or ^b  ) : (static member instance: ^a* ^b  -> _) (a,b )) 
let inline i3 (a:^a,b:^b,c:^c) =               
    ((^a or ^b or ^c) : (static member instance: ^a* ^b* ^c -> _) (a,b,c)) 

type T = T with 
    static member inline instance (a:'a  ) = 
     fun x -> i2(a , Unchecked.defaultof<'r>) x :'r 
    static member inline instance (a:'a, b:'b) = 
     fun x -> i3(a, b, Unchecked.defaultof<'r>) x :'r 


type Return = Return with 
    static member instance (_Monad:Return, _:option<'a>) = fun x -> Some x 
    static member instance (_Monad:Return, _:list<'a> ) = fun x -> [x] 
    static member instance (_Monad:Return, _: 'r -> 'a) = fun x _ -> x 
let inline return' x = T.instance Return x 

type Bind = Bind with 
    static member instance (_Monad:Bind, x:option<_>, _:option<'b>) = fun f -> 
     Option.bind f x 
    static member instance (_Monad:Bind, x:list<_> , _:list<'b> ) = fun f -> 
     List.collect f x 
    static member instance (_Monad:Bind, f:'r->'a, _:'r->'b) = fun k r -> k (f r) r 
let inline (>>=) x (f:_->'R) : 'R = T.instance (Bind, x) f 
let inline (>=>) f g x = f x >>= g 

type Kleisli<'a, 'm> = Kleisli of ('a -> 'm) 
let runKleisli (Kleisli f) = f 

type Id = Id with 
    static member  instance (_Category:Id, _: 'r -> 'r ) = fun() -> id 
    static member inline instance (_Category:Id, _:Kleisli<'a,'b>) = fun() -> 
     Kleisli return' 
let inline id'() = T.instance Id() 

type Comp = Comp with 
    static member  instance (_Category:Comp,   f, _) = (<<) f 
    static member inline instance (_Category:Comp, Kleisli f, _) = 
     fun (Kleisli g) -> Kleisli (g >=> f) 

let inline (<<<) f g = T.instance (Comp, f) g 
let inline (>>>) g f = T.instance (Comp, f) g 

type Arr = Arr with 
    static member  instance (_Arrow:Arr, _: _ -> _) = fun (f:_->_) -> f 
    static member inline instance (_Arrow:Arr, _:Kleisli<_,_>) = 
     fun f -> Kleisli (return' <<< f) 
let inline arr f = T.instance Arr f 

type First = First with 
    static member  instance (_Arrow:First, f, _: 'a -> 'b) = 
     fun() (x,y) -> (f x, y) 
    static member inline instance (_Arrow:First, Kleisli f, _:Kleisli<_,_>) = 
     fun() -> Kleisli (fun (b,d) -> f b >>= fun c -> return' (c,d)) 
let inline first f = T.instance (First, f)() 

let inline second f = let swap (x,y) = (y,x) in arr swap >>> first f >>> arr swap 
let inline (***) f g = first f >>> second g 
let inline (&&&) f g = arr (fun b -> (b,b)) >>> f *** g 

Использование:

> let f = Kleisli (fun y -> [y;y*2;y*3]) <<< Kleisli (fun x -> [ x + 3 ; x * 2 ]) ;; 
val f : Kleisli<int,int list> = Kleisli <fun:[email protected]> 

> runKleisli f <| 5 ;; 
val it : int list = [8; 16; 24; 10; 20; 30] 

> (arr (fun y -> [y;y*2;y*3])) 3 ;; 
val it : int list = [3; 6; 9] 

> let (x:option<_>) = runKleisli (arr (fun y -> [y;y*2;y*3])) 2 ;; 
val x : int list option = Some [2; 4; 6] 

> ((*) 100) *** ((+) 9) <| (5,10) ;; 
val it : int * int = (500, 19) 

> ((*) 100) &&& ((+) 9) <| 5 ;; 
val it : int * int = (500, 14) 

> let x:List<_> = (runKleisli (id'())) 5 ;; 
val x : List<int> = [5] 

Примечание: используйте id'() вместо id

Update: вам нужно F # 3.0 компилировать этот код, иначе here's the F# 2.0 version.

И here's подробное объяснение этой техники, которое является безопасным по типу, расширяемым и, как вы можете видеть, работает даже с некоторыми более крупными типе.

+1

Из любопытства, что такое F # 3.0 для этого кода? –

+1

Функция @GustavoGuerra i3 не будет компилироваться в F # 2.0 из-за ошибки парсера. Существует обходной путь с использованием операторов, но код становится менее читаемым. – Gustavo

+0

[http://www.nut-cracker.com.ar/index.php] («подробное объяснение») не работает. – Noein

29

Мой краткий ответ:

OO не является достаточно мощным, чтобы заменить классы типа.

Самый простой перевод - передать словарь операций, как в одной типичной реализации типа. То есть, если класс типы Foo определяет три метода, а затем определить тип класса/запись с именем Foo, а затем изменить функции из

Foo a => yadda -> yadda -> yadda 

функций как

Foo -> yadda -> yadda -> yadda 

и на каждом месте вызова вы знаете, бетон «экземпляр» для передачи по типу на сайте вызова.

Вот краткий пример того, что я имею в виду:

// typeclass 
type Showable<'a> = { show : 'a -> unit; showPretty : 'a -> unit } //' 

// instances 
let IntShowable = 
    { show = printfn "%d"; showPretty = (fun i -> printfn "pretty %d" i) } 
let StringShowable = 
    { show = printfn "%s"; showPretty = (fun s -> printfn "<<%s>>" s) } 

// function using typeclass constraint 
// Showable a => [a] ->() 
let ShowAllPretty (s:Showable<'a>) l = //' 
    l |> List.iter s.showPretty 

// callsites 
ShowAllPretty IntShowable [1;2;3] 
ShowAllPretty StringShowable ["foo";"bar"] 

Смотрите также

https://web.archive.org/web/20081017141728/http://blog.matthewdoig.com/?p=112

+0

Хммм ... Я думаю, что я видел что-то подобное в отношении класса матрицы F #, где у него есть словарь , который рассказал матрице, которая использует интерфейсы-вспомогательные классы для использования в математике Тип, который был в Матрице. – CodexArcanum

+0

Так что, если бы я хотел дублировать класс типа «Display», я мог бы сделать это как: «val show: Type -> IDisplay», затем «let Showable = Dict » и, наконец, реализовать интерфейс IDisplay на моем вспомогательном стиле Foo, добавьте тип Foo и помощник в словарь, затем попробуйте метод show показать помощник в словаре и вызвать правильный метод? – CodexArcanum

+49

Кроме того, мне смешно, что, когда я впервые попал в Haskell, я думал, что типы классов были crappy интерфейсами с плохим инкапсуляцией; и теперь я начинаю думать, что объекты - это дрянные типы классов с плохим полиморфизмом. «Они» были правы, когда я читал, что обучение FP разрушит вас для ООП. – CodexArcanum

Смежные вопросы