3

How can the following data type from Haskell be expressed in OCaml or SML?

newtype Fix f = In (f (Fix f))
duplode
  • 31,361
  • 7
  • 69
  • 130
Romildo
  • 465
  • 6
  • 18
  • 1
    I suggest you take a look at http://stackoverflow.com/questions/1986374/higher-order-type-constructors-and-functors-in-ocaml in particular, using modules and functors. – Ptival Oct 21 '12 at 05:27

2 Answers2

17

I already answered this question on the mailing-list (and I must say I am mildly displeased that you ask the question in two different places without a good couple of day of delay, because of the duplication of efforts it could provoke), but let's reproduce it here.

There is a difficulty here because OCaml doesn't support higher-ranked type variables. In this declaration, f is not a "type", but a "type operator" (kind * -> *). To do the same in OCaml, you can use a functor (not a Haskell functor; in OCaml the word "functor" denotes a higher-order module that may depend on other modules/functors); functors are higher-kinded.

module type ParamType = sig
  type ('a, 'b) t
end

module Fix (M : ParamType) = struct
  type 'b fix = In of ('b fix, 'b) M.t
end

module List = struct
  module Param = struct
    type ('a, 'b) t = Nil | Cons of 'b * 'a
  end
  include Fix(Param)
end

open List.Param
open List

let rec to_usual_list =
  function
  | In Nil -> []
  | In (Cons (x, xs)) -> x :: to_usual_list xs

The good news is that OCaml also supports equi-recursive rather than iso-recursive types, which allows you to remove the "In" wrapper at each recursion layer. For that you must compile the incumbing module (and all the modules that also see this equirecursion through an interface) with the "-rectypes" option. Then you can write:

module type ParamType = sig
  type ('a, 'b) t
end

module EqFix (M : ParamType) = struct
  type 'b fix = ('b fix, 'b) M.t
end

module EqList = struct
  module Param = struct
    type ('a, 'b) t = Nil | Cons of 'b * 'a
  end
  include EqFix(Param)
end

open EqList.Param

let rec to_usual_list =
  function
  | Nil -> []
  | (Cons (x, xs)) -> x :: to_usual_list xs

The syntax of modules is quite heavy and could appear frightening. If you insist you can use first-class modules to move some of these uses from functors to simple functions. I choose to begin with the "simple" way to do it first.

Higher-kinded variables envy is probably the most severe illness about OCaml type worshippers (or Haskellers that for some (good!) reason come to wander in these parts of Functional County). In practice we do without it with not too much problems, but heavy use of monad transformers would be complicated indeed by this functor step, which is one of the reason it's not a very popular style around here. You may also distract yourself by thinking about the imperfections of higher-kinded variables in the languages that do support them; the limitation to constructor polymorphism rather than arbitrary type-level functions make them less expressive than you would like. The day we work out the details of the absolutely perfect higher-order type abstraction, maybe OCaml will jump to it?

gasche
  • 30,299
  • 2
  • 72
  • 94
  • 1
    I can only thank you for answering and I apologize for posting the question in two different places. When posting it I thought the chance of getting a quick answer was low and I would like to get the broadest audience. Sorry for the inconvenience this may have caused. In the future I will be more cautious about that. – Romildo Oct 21 '12 at 09:55
  • 1
    No harm done! I was a bit grumpy and it's good to let people know so that they don't overdo it, but in return I must thank you for asking an interesting question. – gasche Oct 21 '12 at 10:18
  • 1
    @Romildo: I don't think you should apologize for doing that, since the question is indeed interesting and both audiences could have been supplied with the answer from the other, whichever came first or was better. Promise to do that instead, and you can ignore complaining people with a clean conscience. :-) – Sarah Oct 21 '12 at 12:17
  • 2
    @Sarah that's just wrong, given that there were no link between both questions that would allow people on one side to notice the answer given on the other, and build on them rather than duplicating work. Asking the same questions in two different places without explicit links between them reflects, in fact, that you consider the time spent by the people answering less valuable that the time you can save yourself by parallelization. I understand that a beginner didn't think about it and it's okay. But if you aware of the problem and you still do it, it is egocentric, rude and disrespectful. – gasche Oct 21 '12 at 12:32
  • 1
    @gasche: Your argument holds if the question is truly disinteresting the "the other community" and only holds importance to the asker. I would say StackOverflow is not such a place in general. I don't know about the list. Sharing the response on either forum to the other, is of course, necessary. Anyway, I disagree, but you can certainly think of me whatever you like. – Sarah Oct 21 '12 at 12:46
  • @Sarah: I do not think anything of you, what I described as egocentric and disrespectful is the fact of asking the same question in different places without links between them, once one has been informed of the waste caused by this practice. I would certainly not assume that you (or anyone) are doing this. Please accept my apologies if you interpreted anything I said as an insult -- of course this holds for the Romildo as well. – gasche Oct 21 '12 at 13:08
2

I don't think OCaml lets you abstract over type constructors. For particular applications of Fix you can get a similar effect using -rectypes, I think.

$ ghci
GHCi, version 7.4.2: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude> newtype Fix f = In (f (Fix f))
Prelude> type L = Fix []
Prelude> let w = In [] :: L
Prelude> let x = In [x] :: L

$ ocaml -rectypes
        OCaml version 4.00.0

# type l = l list;;
type l = l list
# ([] : l);;
- : l = []
# let rec x = [x];;
val x : 'a list as 'a = [[[...]]]
# (x : l);;
- : l = [[[...]]]

I am not a module type expert. Probably there's a way to use modules to get closer than this. Everything seems possible using the module system.

Jeffrey Scofield
  • 57,655
  • 2
  • 63
  • 92