2013-02-26 8 views
12

Powiedzmy mam listę opcji:OCaml: wyższe polimorfizm kinded (abstrahując nad modułów?)

let opts = [Some 1; None; Some 4] 

chciałbym przekształcić je w opcji z listy, tak że:

  • Jeśli na liście znajduje się None, wynikiem jest None
  • W przeciwnym razie zbierane są różne int.

To stosunkowo proste napisać to w tym konkretnym przypadku (z użyciem Core i modułu Monad):

let sequence foo = 
let open Option in 
let open Monad_infix in 
    List.fold ~init:(return []) ~f:(fun acc x -> 
    acc >>= fun acc' -> 
    x >>= fun x' -> 
    return (x' :: acc') 
    ) foo;; 

Jednakże, jak sugeruje tytuł pytanie, ja naprawdę chciałbym streszczenie nad typem konstruktor, a nie specjalizuje się w Option. Rdzeń wydaje się używać funktora, aby dać efekt wyższego rodzaju, ale nie wiem, w jaki sposób mogę napisać funkcję, która ma być wyodrębniona przez moduł. W Scali użyłbym niejawnego kontekstu, który wymagałby dostępności około Monad[M[_]]. Spodziewam się, że nie ma możliwości niejawnego przejścia w module, ale jak mam to wyraźnie zrobić? Innymi słowy, czy mogę napisać coś w przybliżeniu:

let sequence (module M : Monad.S) foo = 
let open M in 
let open M.Monad_infix in 
    List.fold ~init:(return []) ~f:(fun acc x -> 
    acc >>= fun acc' -> 
    x >>= fun x' -> 
    return (x' :: acc') 
    ) foo;; 

Czy jest to coś, co można zrobić za pomocą modułów pierwszej klasy?

Edycja: Okay, więc nie przyszło mi do głowy, że próbuję użyć tego konkretnego kodu i wygląda na to, że jest bliższy pracy, niż się spodziewałem! Wydaje się, że składnia jest rzeczywiście ważne, ale uzyskać ten wynik:

Error: This expression has type 'a M.t but an expression was expected of type 'a M.t 
The type constructor M.t would escape its scope  

Pierwsza część błędu wydaje się mylące, ponieważ pasują do siebie, więc zgaduję, problem jest z drugim - tu jest problem że wydaje się, że typ zwrotu nie jest określony? Przypuszczam, że zależy to od modułu, który jest przekazywany - czy to jest problem? Czy istnieje sposób naprawienia tej implementacji?

+0

To stare pytanie może ci się przydać: http://stackoverflow.com/questions/1986374/higher-order-type-konstruktory-i -funektory-w-ocaml – rgrinberg

Odpowiedz

18

pierwsze, tutaj jest samowystarczalny wersja kodu (przy użyciu spuściznę List.fold_left z biblioteki standardowej) dla osób, które nie mają rdzeń pod ręką i nadal chcą spróbować skompilować przykład.

module type MonadSig = sig 
    type 'a t 
    val bind : 'a t -> ('a -> 'b t) -> 'b t 
    val return : 'a -> 'a t 
end 

let sequence (module M : MonadSig) foo = 
    let open M in 
    let (>>=) = bind in 
    List.fold_left (fun acc x -> 
    acc >>= fun acc' -> 
    x >>= fun x' -> 
    return (x' :: acc') 
) (return []) foo;; 

Komunikat o błędzie, że masz środki (mylące pierwsza linia może być ignorowane), że definicja Mt jest lokalny moduł M i nie może uciec jej zakresu, to z tym, co "Próbuję napisać .

To dlatego, że używasz modułów pierwszej klasy, które pozwalają na streszczenie na moduły, ale nie mieć typy zależne wyglądzie takim jak zwracany typ zależy od wartości modułu argumentu, albo przynajmniej ścieżka (tutaj M).

Rozważmy następujący przykład:

module type Type = sig 
    type t 
end 

let identity (module T : Type) (x : T.t) = x 

To jest złe.Punkty Komunikaty o błędach na (x : T.t) i mówi:

Error: This pattern matches values of type T.t 
     but a pattern was expected which matches values of type T.t 
     The type constructor T.t would escape its scope 

Co może zrobić to streszczenie od pożądanego typu abstrakcyjnego przed wami na pierwszej klasy modułu T, tak że nie ma już ucieczki.

let identity (type a) (module T : Type with type t = a) (x : a) = x 

ta opiera się na zdolności do wyraźnego streszczenie nad typu zmiennej a. Niestety, ta funkcja nie została rozszerzona na abstrakcje w przypadku zmiennych o wyższych parametrach. Aktualnie nie może zapisu:

let sequence (type 'a m) (module M : MonadSig with 'a t = 'a m) (foo : 'a m list) = 
    ... 

Rozwiązaniem jest użycie funktor: zamiast pracować na poziomie wartości, pracujesz na poziomie modułu, który ma bogatszy język rodzaju.

module MonadOps (M : MonadSig) = struct 
    open M 
    let (>>=) = bind 

    let sequence foo = 
    List.fold_left (fun acc x -> 
     acc >>= fun acc' -> 
     x >>= fun x' -> 
     return (x' :: acc') 
    ) (return []) foo;; 
end 

Zamiast każdą operację jednowartościowy (sequence, map itd) abstrakt nad monady, zrobisz abstrakcję moduł szerokości.

+0

Tak, doszedłem do sedna przy założeniu, że przyczyną problemu był typ zależny (-ish)! Dzięki za bardzo szczegółową odpowiedź. – Impredicative