caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* Monads, monadic notation and OCaml
@ 2005-03-28  2:31 Jacques Carette
  2005-03-28 18:17 ` [MetaOCaml] " Walid Taha
  0 siblings, 1 reply; 4+ messages in thread
From: Jacques Carette @ 2005-03-28  2:31 UTC (permalink / raw)
  To: caml-list; +Cc: metaocaml-users

[-- Attachment #1: Type: text/plain, Size: 2582 bytes --]

Attached is a camlp4 syntax extension for a 'monadic do notation' for OCaml, much like Haskell's.  The syntax 
extension is joint work with Oleg Kiselyov, and is loosely based on previous work of Lydia van Dijk on a similar 
syntax extension.

Naturally, in OCaml certain monads (like State and IO) are unnecessary.  But other monads (List, option, etc) can be 
quite convenient.

But monads can be much more than convenient.  Below is some code written in a non-deterministic monad of streams.
This example is particularly interesting in that it cannot be (naively) done in either Prolog or in Haskell's
MonadPlus monad, both of which would go into an infinite loop on this example.

(* test non-determinism monad, the simplest possible implementation *)
type 'a stream = Nil | Cons of 'a * (unit -> 'a stream) 
                      | InC of (unit -> 'a stream)
let test_nondet () =
   let mfail = fun () -> Nil in
   let ret a = fun () -> Cons (a,mfail) in
   (* actually, interleave: a fair disjunction with breadth-first search*)
   let rec mplus a b = fun () -> match a () with
                   | Nil -> InC b
		  | InC a -> (match b () with
		    | Nil -> InC a
		    | InC b -> InC (mplus a b)
		    | Cons (b1,b2) -> Cons (b1, (mplus a b2)))
                   | Cons (a1,a2) -> Cons (a1,(mplus b a2)) in
   (* a fair conjunction *)
   let rec bind m f = fun () -> match m () with
                   | Nil -> mfail ()
		  | InC a -> InC (bind a f)
                   | Cons (a,b) -> mplus (f a) (bind b f) () in
   let guard be = if be then ret () else mfail in
   let rec run n m = if n = 0 then [] else
                 match m () with
		| Nil -> []
		| InC a -> run n a
		| Cons (a,b) -> (a::run (n-1) b)
   in
   let rec numb () = InC (mplus (ret 0) (mdo { n <-- numb; ret (n+1) })) in
   (* Don't try this in Prolog or in Haskell's MonadPlus! *)
   let tst = mdo {
                   i <-- numb;
                   guard (i>0);
                   j <-- numb;
                   guard (j>0);
                   k <-- numb;
                   guard (k>0);
                   (* Just to illustrate the `let' form within mdo *)
                   let test x = x*x = j*j + k*k in;
                   guard (test i);
		  ret (i,j,k)
                 } 
   in run 7 tst
;;

We ourselves have been experimenting with a combined state-passing, continuation-passing monad, where the values 
returned and manipulated are code fragments (in MetaOCaml).  This allows for considerably simpler, typed code 
combinators.  Details of this work will be reported elsewhere.  

Jacques

[-- Attachment #2: pa_monad.ml --]
[-- Type: application/octet-stream, Size: 4477 bytes --]

(* name:          pa_monad.ml
 * synopsis:      Haskell-like "do" for monads
 * authors:       Jacques Carette and Oleg Kiselyov, 
 *                based in part of work of Lydia E. Van Dijk
 * last revision: Sun Mar 27 2005
 * ocaml version: 3.08.0 *)


(** Conversion Rules

Grammar informally:
                 mdo { exp }
                 mdo { exp1; exp2 }
                 mdo { x <-- exp; exp }
                 mdo { let x = foo in; exp }
which is almost literally the grammar of Haskell `do' notation,
modulo `do'/`mdo' and `<-'/`<--'.

Grammar formally:

        mdo { <do-body> }
        <do-body> :: =
                "let" var = EXP ("and" var = EXP)* "in" ";" <do-body>
                EXP
                (pat <--)? EXP ";" <do-body>

Semantics (as re-writing into the core language)

        mdo { exp } ===> exp
        mdo { pat <-- exp; rest } ===> bind exp (fun pat -> mdo { rest })
        mdo { exp; rest } ===> bind exp (fun _ -> mdo { rest })
        mdo { let pat = exp in; rest } ===> let pat = exp in mdo { rest }

Actually, in `let pat = exp' one can use anything that is allowed
in a `let' expression, e.g., `let pat1 = exp1 and pat2 = exp2 ...'.
The reason we can't terminate the `let' expression with just a semi-colon
is because semi-colon can be a part of an expression that is bound to
the pattern.

It is possible to use `<-' instead of `<--'. In that case,
the similarity to the `do' notation of Haskell will be complete. However,
due to the parsing rules of Camlp4, we would have to accept `:=' as
an alias for `<-'. So, mdo { pat := exp1; exp2 } would be allowed too.
Perhaps that is too much.

The major difficulty with the `do' notation is that it can't truly be
parsed by an LR-grammar. Indeed, to figure out if we should start
parsing <do-body> as an expression or a pattern, we have to parse it
as a pattern and check the "<--" delimiter. If it isn't there, we should
_backtrack_ and parse it again as an expression. Furthermore, "a <-- b"
(or "a <- b") can also be parsed as an expression. However, for some patterns,
e.g. (`_ <-- exp'), that cannot be parsed as an expression. 

    *)

type monbind = BindL of (MLast.patt * MLast.expr) list
             | BindM of MLast.patt * MLast.expr
             | ExpM  of MLast.expr
(* Convert MLast.expr into MLast.patt, if we `accidentally'
   parsed a pattern as an expression.
  The code is based on pattern_eq_expression in 
  /camlp4/etc/pa_fstream.ml *)

let rec exp_to_patt loc e =
  match e with
    <:expr< $lid:b$ >> -> <:patt< $lid:b$ >>
  | <:expr< $uid:b$ >> -> <:patt< $uid:b$ >>
  | <:expr< $e1$ $e2$ >> -> 
      let p1 = exp_to_patt loc e1 and p2 = exp_to_patt loc e2 in
      <:patt< $p1$ $p2$ >>
  | <:expr< ($list:el$) >> ->
      let pl = List.map (exp_to_patt loc) el in
      <:patt< ($list:pl$) >>
  | _ -> failwith "This pattern isn't yet supported"

(* The main semantic function *)
let process loc b = 
    let globbind2 x p acc =
        <:expr< bind $x$ (fun $p$ -> $acc$) >>
    and globbind1 x acc =
        <:expr< bind $x$ (fun _ -> $acc$) >>
    and ret n = <:expr< $n$ >> in
    let folder = let (a,b) = (globbind2, globbind1) in
        (fun accumulator y -> 
        match y with
        | BindM(p,x) -> a x p accumulator
        | ExpM(x) -> b x accumulator
        | BindL(l) -> <:expr< let $list:l$ in $accumulator$ >>
        )
    in
    match List.rev b with 
    | [] -> failwith "somehow got an empty list from a LIST1!"
    | (ExpM(n)::t) -> List.fold_left folder (ret n) t  
    | _ -> failwith "Does not end with an expression"

EXTEND
    GLOBAL: Pcaml.expr; 

    Pcaml.expr: LEVEL "expr1"
    [
      [ "mdo"; "{";
        bindings = LIST1 monadic_binding SEP ";"; "}" ->
            process loc bindings
      ] 
    ] ;

    Pcaml.expr: BEFORE "apply"
    [ NONA
	  [ e1 = SELF; "<--"; e2 = Pcaml.expr LEVEL "expr1" ->
          <:expr< $e1$ $lid:"<--"$ $e2$ >>
	  ] 
    ] ;

    monadic_binding:
    [ 
      [ "let"; l = LIST1 Pcaml.let_binding SEP "and"; "in" ->
	      BindL(l) ]
    | 
      [ x = Pcaml.expr LEVEL "expr1" ->
	(* For some patterns, "patt <-- exp" can parse
	   as an expression too. So we have to figure out which is which. *)
        match x with
              <:expr< $e1$  $lid:op$  $e3$  >> when op = "<--" 
                  -> BindM((exp_to_patt loc e1),e3)
            | _ -> ExpM(x) ]
    | 
      [ p = Pcaml.patt LEVEL "simple"; "<--"; x = Pcaml.expr LEVEL "expr1" ->
        BindM(p,x) ]
    ] ;
END;

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [MetaOCaml] Monads, monadic notation and OCaml
  2005-03-28  2:31 Monads, monadic notation and OCaml Jacques Carette
@ 2005-03-28 18:17 ` Walid Taha
  2005-03-28 18:40   ` Jacques Carette
  0 siblings, 1 reply; 4+ messages in thread
From: Walid Taha @ 2005-03-28 18:17 UTC (permalink / raw)
  To: Jacques Carette; +Cc: caml-list, metaocaml-users


This is pretty cool!

Are you using the same monad we used in the FFT work?

Also, can you either get rid of the ";" after the "in" or the "in" before
the ";"?  :)

Walid.

On Sun, 27 Mar 2005, Jacques Carette wrote:

|Attached is a camlp4 syntax extension for a 'monadic do notation' for OCaml, much like Haskell's.  The syntax
|extension is joint work with Oleg Kiselyov, and is loosely based on previous work of Lydia van Dijk on a similar
|syntax extension.
|
|Naturally, in OCaml certain monads (like State and IO) are unnecessary.  But other monads (List, option, etc) can be
|quite convenient.
|
|But monads can be much more than convenient.  Below is some code written in a non-deterministic monad of streams.
|This example is particularly interesting in that it cannot be (naively) done in either Prolog or in Haskell's
|MonadPlus monad, both of which would go into an infinite loop on this example.
|
|(* test non-determinism monad, the simplest possible implementation *)
|type 'a stream = Nil | Cons of 'a * (unit -> 'a stream)
|                      | InC of (unit -> 'a stream)
|let test_nondet () =
|   let mfail = fun () -> Nil in
|   let ret a = fun () -> Cons (a,mfail) in
|   (* actually, interleave: a fair disjunction with breadth-first search*)
|   let rec mplus a b = fun () -> match a () with
|                   | Nil -> InC b
|		  | InC a -> (match b () with
|		    | Nil -> InC a
|		    | InC b -> InC (mplus a b)
|		    | Cons (b1,b2) -> Cons (b1, (mplus a b2)))
|                   | Cons (a1,a2) -> Cons (a1,(mplus b a2)) in
|   (* a fair conjunction *)
|   let rec bind m f = fun () -> match m () with
|                   | Nil -> mfail ()
|		  | InC a -> InC (bind a f)
|                   | Cons (a,b) -> mplus (f a) (bind b f) () in
|   let guard be = if be then ret () else mfail in
|   let rec run n m = if n = 0 then [] else
|                 match m () with
|		| Nil -> []
|		| InC a -> run n a
|		| Cons (a,b) -> (a::run (n-1) b)
|   in
|   let rec numb () = InC (mplus (ret 0) (mdo { n <-- numb; ret (n+1) })) in
|   (* Don't try this in Prolog or in Haskell's MonadPlus! *)
|   let tst = mdo {
|                   i <-- numb;
|                   guard (i>0);
|                   j <-- numb;
|                   guard (j>0);
|                   k <-- numb;
|                   guard (k>0);
|                   (* Just to illustrate the `let' form within mdo *)
|                   let test x = x*x = j*j + k*k in;
|                   guard (test i);
|		  ret (i,j,k)
|                 }
|   in run 7 tst
|;;
|
|We ourselves have been experimenting with a combined state-passing, continuation-passing monad, where the values
|returned and manipulated are code fragments (in MetaOCaml).  This allows for considerably simpler, typed code
|combinators.  Details of this work will be reported elsewhere.
|
|Jacques
|
|
|!DSPAM:42476c8f13209207723021!
|


^ permalink raw reply	[flat|nested] 4+ messages in thread

* RE: [MetaOCaml] Monads, monadic notation and OCaml
  2005-03-28 18:17 ` [MetaOCaml] " Walid Taha
@ 2005-03-28 18:40   ` Jacques Carette
  0 siblings, 0 replies; 4+ messages in thread
From: Jacques Carette @ 2005-03-28 18:40 UTC (permalink / raw)
  To: 'Walid Taha'; +Cc: caml-list, metaocaml-users

> This is pretty cool!

Thanks.

> Are you using the same monad we used in the FFT work?

Yes.  For those on caml-list, the paper Walid refers to is
@inproceedings{KiselyovSwadiTaha2004,
	author = {Oleg Kiselyov and Kedar N.~Swadi and Walid Taha},
	title = {A methodology for generating verified combinatorial
circuits},
	booktitle = {ACM International conference on Embedded Software
(EMSOFT)},
	year = {2004}
}

> Also, can you either get rid of the ";" after the "in" or the "in" before
the ";"?  :)

See the comments in pa_monad.ml.  Unfortunately we cannot easily do that; in
fact the required amount of work to achieve this might be quite large!  It
would be by far easier to integrate this right into the grammar rather than
to post-facto extend the grammar via camlp4.  At least, as far as my current
understanding of camlp4 goes -- maybe if a lot more of the tricks of the
camlp4 Masters were documented, it would not look so hard anymore!

Jacques


^ permalink raw reply	[flat|nested] 4+ messages in thread

* RE: [MetaOCaml] Monads, monadic notation and OCaml
@ 2005-03-28 18:39 Harrison, William L.
  0 siblings, 0 replies; 4+ messages in thread
From: Harrison, William L. @ 2005-03-28 18:39 UTC (permalink / raw)
  To: Walid Taha, Jacques Carette; +Cc: metaocaml-users, caml-list

Hi Jacques and Walid-
 
<PLUG>
     I've been reading your recent posts, and given the interest in stream (a.k.a. resumption) monads, I thought you might be interested in a draft I've submitted about the use of just such monads:
                                 http://www.cs.missouri.edu/~harrison/drafts/CheapThreads.pdf
This article describes the construction of a multitasking operating system kernel in Haskell 98 using resumption monads; the kernel has all the "usual" OS behaviors (e.g., process forking, preemption, message passing, synchronization, etc.) captured succintly.
</PLUG>
 
Bill

________________________________

From: metaocaml-users-l-bounces@mailman.rice.edu on behalf of Walid Taha
Sent: Mon 3/28/2005 12:17 PM
To: Jacques Carette
Cc: metaocaml-users@cs.rice.edu; caml-list@inria.fr
Subject: Re: [MetaOCaml] Monads, monadic notation and OCaml




This is pretty cool!

Are you using the same monad we used in the FFT work?

Also, can you either get rid of the ";" after the "in" or the "in" before
the ";"?  :)

Walid.

On Sun, 27 Mar 2005, Jacques Carette wrote:

|Attached is a camlp4 syntax extension for a 'monadic do notation' for OCaml, much like Haskell's.  The syntax
|extension is joint work with Oleg Kiselyov, and is loosely based on previous work of Lydia van Dijk on a similar
|syntax extension.
|
|Naturally, in OCaml certain monads (like State and IO) are unnecessary.  But other monads (List, option, etc) can be
|quite convenient.
|
|But monads can be much more than convenient.  Below is some code written in a non-deterministic monad of streams.
|This example is particularly interesting in that it cannot be (naively) done in either Prolog or in Haskell's
|MonadPlus monad, both of which would go into an infinite loop on this example.
|
|(* test non-determinism monad, the simplest possible implementation *)
|type 'a stream = Nil | Cons of 'a * (unit -> 'a stream)
|                      | InC of (unit -> 'a stream)
|let test_nondet () =
|   let mfail = fun () -> Nil in
|   let ret a = fun () -> Cons (a,mfail) in
|   (* actually, interleave: a fair disjunction with breadth-first search*)
|   let rec mplus a b = fun () -> match a () with
|                   | Nil -> InC b
|                 | InC a -> (match b () with
|                   | Nil -> InC a
|                   | InC b -> InC (mplus a b)
|                   | Cons (b1,b2) -> Cons (b1, (mplus a b2)))
|                   | Cons (a1,a2) -> Cons (a1,(mplus b a2)) in
|   (* a fair conjunction *)
|   let rec bind m f = fun () -> match m () with
|                   | Nil -> mfail ()
|                 | InC a -> InC (bind a f)
|                   | Cons (a,b) -> mplus (f a) (bind b f) () in
|   let guard be = if be then ret () else mfail in
|   let rec run n m = if n = 0 then [] else
|                 match m () with
|               | Nil -> []
|               | InC a -> run n a
|               | Cons (a,b) -> (a::run (n-1) b)
|   in
|   let rec numb () = InC (mplus (ret 0) (mdo { n <-- numb; ret (n+1) })) in
|   (* Don't try this in Prolog or in Haskell's MonadPlus! *)
|   let tst = mdo {
|                   i <-- numb;
|                   guard (i>0);
|                   j <-- numb;
|                   guard (j>0);
|                   k <-- numb;
|                   guard (k>0);
|                   (* Just to illustrate the `let' form within mdo *)
|                   let test x = x*x = j*j + k*k in;
|                   guard (test i);
|                 ret (i,j,k)
|                 }
|   in run 7 tst
|;;
|
|We ourselves have been experimenting with a combined state-passing, continuation-passing monad, where the values
|returned and manipulated are code fragments (in MetaOCaml).  This allows for considerably simpler, typed code
|combinators.  Details of this work will be reported elsewhere.
|
|Jacques
|
|
|!DSPAM:42476c8f13209207723021!
|
_______________________________________________
metaocaml-users-L mailing list
metaocaml-users-L@mailman.rice.edu
https://mailman.rice.edu/mailman/listinfo/metaocaml-users-l



^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2005-03-28 18:42 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2005-03-28  2:31 Monads, monadic notation and OCaml Jacques Carette
2005-03-28 18:17 ` [MetaOCaml] " Walid Taha
2005-03-28 18:40   ` Jacques Carette
2005-03-28 18:39 Harrison, William L.

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).