caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
From: Gabriel Scherer <gabriel.scherer@gmail.com>
To: Dmitry Grebeniuk <gdsfh1@gmail.com>
Cc: Jacques Garrigue <caml-list@inria.fr>
Subject: Re: [Caml-list] recursive module, object types, tying knot
Date: Tue, 13 Sep 2011 09:22:52 +0200	[thread overview]
Message-ID: <CAPFanBF5trsSp74sVEVGKqYK_Ryp9ZadCi_Kq2YQv2HwRpmU3A@mail.gmail.com> (raw)
In-Reply-To: <CAPi0vKX27x8pgMi9uHCRdAK0tBfZ7A9PEnfw+gyMNfxHfKSXCA@mail.gmail.com>

On Tue, Sep 13, 2011 at 6:56 AM, Dmitry Grebeniuk <gdsfh1@gmail.com> wrote:
>  I've tried to write some code where the initial container is
> stored in the object itself, but couldn't make it work.  And
> this approach (map = fold_right + cons) is somewhat limited,
> it will work good for single-linked lists, but I can't imagine
> it can work with more complex data structures like trees

You may define more general folds. The idea is that from any algebraic
datatype you can derive a fold operator.

See for example:

  type 'a list = Nil | Cons of 'a * 'a list
  val list_fold : 'r -> ('a -> 'r -> 'r) -> 'r

  type 'a tree = Nil | Leaf of 'a list * 'a * 'a list
  val tree_fold : 'r -> ('r -> 'a -> 'r) -> 'r

  type ('a, 'b, 'c) strange_list =
    | End of 'c
    | ACons of 'a * ('a, 'b, 'c) strange_list
    | BCons of 'b * ('a, 'b, 'c) strange_list
  val strange_list_fold : ('c -> 'r) -> ('a -> 'r -> 'r) -> ('b -> r -> r) -> r

The idea is that for each constructor of the datatype, you add an
argument to the fold, that has the same signature as its type as a
function (Nil : 'a list) (Cons :  'a -> 'a list -> 'a list), with
recursive occurences of the type replaced by the elimination type
parameter 'r.

There is a more abstract construction : if you express recursive
datatypes as fixpoint of a type with one more parameter (~ polynomial
functor), you can define fold recursively from a `map` operation on
this type. For example, ('a list) is the fixpoint on 'b of (type ('a,
'b) cons = Nil' | Cons' of 'a * 'b), which is witnessed by the
existence of reciprocal (val wrap : ('a, 'a list) cons -> 'a list) and
(val unwrap : 'a list -> ('a, 'a list cons)), and (some form of)
list_fold can be defined as
  let rec list_fold li = f (map_cons (list_fold f) (unwrap li))

For a more abstract and hardly readable presentation of this topic,
see the article "Functional programming with Bananas, Lenses,
Envelopes and Barbed Wire" by Meijer, Fokkinga and Paterson, 1991.

> (and I can imagine the overhead of folding + consing
> over arrays).  So I prefer to avoid such style of mapping.

An `iter` function is sufficient for arrays:

class type ['a] array = object
  ...
  method length : int
  method iter : (int -> 'a -> unit) -> unit
end

You can then express the map operation:
  let map f t =
    let result = make_array t#length in
    t#iter (fun i x -> result#set i (f x));
    result

An internal map would have to allocate a new array anyway (in the
general case where (f : 'a -> 'b), you can't mutate the array
in-place), so this is as efficient memory-wise. Similarly, a map
defined by the user from a fold on a recursive datatype has the same
allocation profile as an internally-defined fold. The only potential
overhead comes from the passing of the fold function (which means one
more indirect call per node traversed), but if you're at this level of
detail you may not want to design your core data structures as object
anyway.


      reply	other threads:[~2011-09-13  7:23 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-09-07 21:29 Dmitry Grebeniuk
2011-09-07 21:44 ` Jacques Garrigue
2011-09-09 11:14   ` Dmitry Grebeniuk
2011-09-09 13:50     ` Jacques Garrigue
2011-09-13  4:56       ` Dmitry Grebeniuk
2011-09-13  7:22         ` Gabriel Scherer [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAPFanBF5trsSp74sVEVGKqYK_Ryp9ZadCi_Kq2YQv2HwRpmU3A@mail.gmail.com \
    --to=gabriel.scherer@gmail.com \
    --cc=caml-list@inria.fr \
    --cc=gdsfh1@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).