2

Since the data/monad module's do notation operates on structures, how can I define monad types that are functions, e.g. like parsers?

I'm used to OCaml, where my monad would have had roughly the following signature:

module type Parser = sig
  type state = string * int
  type 'a t = state -> (('a * state), string) Result.t

  val return: 'a -> 'a t
  val bind: 'a t -> ('a -> 'b t) -> 'b t
end

I'm sorry to post an example in OCaml, my racket abilities are not great for now.

Is this kind of monad compatible with data/monad, or should I look at another solution?

Antoine
  • 1,782
  • 1
  • 14
  • 32

1 Answers1

3

There’s nothing that prevents you from wrapping a function in a structure, then implementing the gen:monad interface on that structure. The megaparsack library is an example that uses that technique to implement a monadic parser combinator library. Specifically, take a look at the parser structure definition:

(struct parser (proc)
  #:methods gen:functor
  [(define/generic -map map)
   (define (map f p)
     (parser (compose (match-lambda [(consumed (ok v rest message)) (consumed (ok (-map f v) rest message))]
                                    [(empty (ok v rest message))    (empty (ok (-map f v) rest message))]
                                    [error                          error])
                      (parser-proc p))))]

  #:methods gen:applicative
  [(define (pure _ x)
     (pure/p x))

   (define (apply p ps)
     (do [f  <- p]
         [xs <- (map/m values ps)]
         (d:pure (r:apply f xs))))]

  #:methods gen:monad
  [(define (chain f p)
     (parser
      (λ (input)
        (match (parse p input)
          [(empty (ok (and foo (syntax-box x _)) rest message))
           (match (parse (f x) rest)
             [(empty reply) (empty (merge-message/reply message reply))]
             [consumed      consumed])]
          [(consumed (ok (and foo (syntax-box x srcloc)) rest message))
           (consumed (match (parse (f x) rest)
                       [(consumed (ok stx rest message))
                        (ok (merge-syntax-box/srcloc stx srcloc) rest message)]
                       [(empty (ok (syntax-box datum _) rest message))
                        (merge-message/reply message (ok (syntax-box datum srcloc) rest message))]
                       [(consumed error) error]
                       [(empty error) (merge-message/reply message error)]))]
          [error error]))))])

This defines a structure type named parser containing a single field, proc, and implements the gen:functor, gen:applicative, and gen:monad interfaces on that structure type. The single field contains the parser procedure.

Alexis King
  • 43,109
  • 15
  • 131
  • 205