caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* map and fold
@ 2006-12-22 11:18 skaller
  2006-12-22 12:12 ` [Caml-list] " Florian Hars
  2006-12-23 23:54 ` Andrej Bauer
  0 siblings, 2 replies; 6+ messages in thread
From: skaller @ 2006-12-22 11:18 UTC (permalink / raw)
  To: caml-list

What is the relationship between map and fold?

It seems like if you have a X.fold plus an constructor
which adds an element to an existing container Y.t
you can define a kind of map which copies elements of
'a X.t into 'b T.t given an element map f: 'a -> 'b.

In STL the algorithm accumulate can do this, given
a start and end iterator for the input container,
and an insert iterator for the output container.

Anyhow it 'feels' like the fold is a container destructor,
and the constructor is a kind of 'dual' of fold. Map is then
just a combination of the fold with a special case of
the constructor function.. hmm. Any hint what the formal
symmetry is here?


-- 
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net


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

* Re: [Caml-list] map and fold
  2006-12-22 11:18 map and fold skaller
@ 2006-12-22 12:12 ` Florian Hars
  2006-12-23 23:54 ` Andrej Bauer
  1 sibling, 0 replies; 6+ messages in thread
From: Florian Hars @ 2006-12-22 12:12 UTC (permalink / raw)
  To: skaller; +Cc: caml-list

skaller wrote:
> Any hint what the formal symmetry is here?

Isn't this what the bananas-paper is about?
http://citeseer.ist.psu.edu/meijer91functional.html

Yours, Florian.


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

* Re: [Caml-list] map and fold
  2006-12-22 11:18 map and fold skaller
  2006-12-22 12:12 ` [Caml-list] " Florian Hars
@ 2006-12-23 23:54 ` Andrej Bauer
  2006-12-24  3:26   ` skaller
  2006-12-24  3:42   ` skaller
  1 sibling, 2 replies; 6+ messages in thread
From: Andrej Bauer @ 2006-12-23 23:54 UTC (permalink / raw)
  To: skaller, caml-list

skaller wrote:
> What is the relationship between map and fold?

I don't have much to say about map, but here's a mathematician's 
explanation of where fold comes from. Perhaps it will help.

With each inductive datatype, such as natural numbers, lists or trees, 
there is an associated induction principle, which roughly says:

   If a property P holds for the base cases (zero, empty list, empty
   tree), and if the property is preserved by all the constructors
   (successor, cons), then the property holds for all elements of the
   inductive datatype.

We can interpret the induction principle under the realizability 
interpretation (which is sort of like propositions-as-types) to obtain a 
type. The fold-liek operation corresponding to the inductive datatype 
has this type. Examples follow.


1) Natural numbers are defined inductively as

   type nat = Zero | Succ of nat

The induction principle is:

   P(Zero) ==> (forall n:nat)(P(n) ==> P(Succ(n))) ==>
   (forall n:nat)P(n)

The type corresponding to this is:

   'p -> (nat -> 'p -> 'p) -> nat -> 'p

An element of this type is:

let rec fold u f = function
   | Zero -> u
   | Succ n -> f n (fold u f n)

This is just primitive recursion.

2) Lists (parametrized by 'a) are defined inductively as

   type 'a list = Nil | Cons of 'a t * 'a list

The induction principle is

   P(Nil) ==> (forall x:'a)(forall l:'a list)(P(l) => P(Cons(x,l)) ==>
   (forall l:'a list)P(l)

which yields the type

   'p -> ('a -> 'a list -> 'p -> 'p) -> 'a list -> 'p

with the fold-like operation of this type:

let rec fold u f = function
   | Nil -> u
   | Cons (x,l) -> f x l (fold u f l)

(Note the difference between fold and List.fold_left: fold hands the 
tail to f, whereas List.fold_left does not.)

3) Trees of t's:

type 'a tree = Empty | Node of 'a * 'a tree * 'a tree

Induction principle:

   P(Empty) ==>
   (forall u:'a)(forall t1:'a tree)(forall t2:'a tree)(
     P(t1) ==> P(t2) ==> P (Node (u, t1, t2)
   ) ==>
   (forall t:'a tree)P(t)

The type:

   'p -> ('a -> 'a tree -> 'a tree -> 'p -> 'p -> 'p) -> 'p

Fold for trees:

let rec fold u f = function
   | Empty -> u
   | Tree (x,t1,t2) -> f x t1 t2 (fold u t1) (fold u t2)


Just like induction is a powerful and basic principle, fold is a 
powerful operation that allows us to construct many others.

I am not quite sure how skaller intended map to work (it seems like the 
"add one more element" operation is rather specialized). A simple way to 
view map is as follows.

Suppose we have a parametrized type

   type 'a t = ...

in which 'a appears _covariantly_. Then

   map : ('a -> 'b) -> 'a t -> 'b t

will be just the action of the type constructor t on morphisms (when we 
view things appropriately in a category-theoretic sense, with t being a 
functor). Example:

type 'a t = Foo of (int -> 'a * 'a * t) | Bar of 'a * t

let rec map f = function
   | Foo h -> Foo (fun n -> let u,v,x = h n in (f u, f v, map f x))
   | Bar (u,x) -> Bar (f u, map f x)

Furthermore if t is inductively defined, we can express map in terms of 
fold. Examples:

1) Lists:

type 'a list = Nil | Cons of 'a * 'a list

let map f = fold Nil (fun u _ x -> Cons (f u, x))

2) Trees:

type 'a tree = Empty | Node of 'a * 'a tree * 'a tree

let map f = fold Empty (fun u _ _ x y -> Node (f u, x, y))

Andrej


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

* Re: [Caml-list] map and fold
  2006-12-23 23:54 ` Andrej Bauer
@ 2006-12-24  3:26   ` skaller
  2006-12-24  3:42   ` skaller
  1 sibling, 0 replies; 6+ messages in thread
From: skaller @ 2006-12-24  3:26 UTC (permalink / raw)
  To: Andrej.Bauer; +Cc: caml-list

On Sun, 2006-12-24 at 00:54 +0100, Andrej Bauer wrote:

[]

> I am not quite sure how skaller intended map to work 

Neither am I. My aim here is roughly: in Felix 
I have overloading, typeclasses and I also have tuples.

Overloading provides 'generic' operations on primitive types
in an ad hoc manner.

Typeclasses provide 'generic' operations on primitive types
in a uniform manner.

Tuples are heterogenous lists with statically known types,
more generally we'd have algebraic types but tuples will
do for a start.

There are many things I'd like to do with tuples.
Too many. The operations are generic (polyadic) not merely
polymorphic as for lists.

In fact there is a correspondence: the Ocaml representation
of a tuple is a list of terms, so run time generic operations
are Ocaml time list polymorphic: so in the compiler,
implementing generic operations is easy -- but end users
can't do it, and 'easy' isn't the same as wanting to
provide hundreds of compiler supported intrinsics.

What I need to do is find some primitive, compiler supported
operations, which can be *combined* in user library code
to provide the others.

Well, I obviously need things like

	fold
	zip (compose)
	split
	map

and a host of other generic operations. In fact so far I
have implemented

	memcount e         // counts members
	_tuple_fold f i c  // fold generic f 
        _tuple_trans e     // transpose tuple of tuples
                           // subsumes zip and split

but I have no idea 

(a) whether this set of operations is sufficient
(b) how to combine them 
(c) how to generalise to all algebraic types

so I'm trying to get a more theoretical picture of
how these things relate.

I have a picture that if you have fold, map comes for free,
but just folding over the appropriate constructor.

But really the question is deeper: I'm really asking
how to design a programming language that supports 
generic programming, or more precisely, how to make
practical progress towards that.

I can do slightly more to Felix than Ocaml, since
I can intervene in any part of the compiler. The tuple
fold depends on type information, so it couldn't be
implemented in Ocaml using camlp4.

The users have an immediate need: right now we have

	typeclass Eq[t] { virtual fun eq: t * t -> bool; }

	instance Eq[int] { .. }
	instance Eq[double] { .. }
	...

so we have generic equality for primitives. There should
be no need to manually instantiate the typeclass for
each tuple of interest though: one definition should
be enough to cover them all:

fun eqa[t with Eq[t]]: bool * (t * t) -> bool =
| ?result, (?a, ?b) => result and a == b;

val x1 = ((1,1),(2.0,2.0),("3","3"));
print$ _tuple_fold eqa true x1; endl;

actually works now. Throw in transpose function to get the
arguments in the right form, cover sum types as well,
and we have second class generic equality operator:
this is better than Ocaml's polymorphic equality in the
sense that it also works with abstract types, and weaker
in the sense it is only second class (compile time only,
no closures).

This is pretty hot stuff for a scripting language:
much safer than Python etc.

-- 
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net


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

* Re: [Caml-list] map and fold
  2006-12-23 23:54 ` Andrej Bauer
  2006-12-24  3:26   ` skaller
@ 2006-12-24  3:42   ` skaller
  2006-12-24 11:00     ` Tom
  1 sibling, 1 reply; 6+ messages in thread
From: skaller @ 2006-12-24  3:42 UTC (permalink / raw)
  To: Andrej.Bauer; +Cc: caml-list

On Sun, 2006-12-24 at 00:54 +0100, Andrej Bauer wrote:
> skaller wrote:

> Furthermore if t is inductively defined, we can express map in terms of 
> fold. Examples:
> 
> 1) Lists:
> 
> type 'a list = Nil | Cons of 'a * 'a list
> 
> let map f = fold Nil (fun u _ x -> Cons (f u, x))
> 
> 2) Trees:
> 
> type 'a tree = Empty | Node of 'a * 'a tree * 'a tree
> 
> let map f = fold Empty (fun u _ _ x y -> Node (f u, x, y))

However in Ocaml at least you cannot actually write a single
definition for map in terms of a single fold -- you have to
write out fold for each data type, and worse, even given that
you still need to write out map for each data type too,
following an idiomatic pattern.

How could Ocaml be extended to get rid of this unsafe
verbosity?

Even if the resulting generic operators weren't first class,
it would still be useful to define 'map' once and be done
with it.

-- 
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net


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

* Re: [Caml-list] map and fold
  2006-12-24  3:42   ` skaller
@ 2006-12-24 11:00     ` Tom
  0 siblings, 0 replies; 6+ messages in thread
From: Tom @ 2006-12-24 11:00 UTC (permalink / raw)
  To: skaller; +Cc: Andrej.Bauer, caml-list

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

>
>
> However in Ocaml at least you cannot actually write a single
> definition for map in terms of a single fold -- you have to
> write out fold for each data type, and worse, even given that
> you still need to write out map for each data type too,
> following an idiomatic pattern.
>
> How could Ocaml be extended to get rid of this unsafe
> verbosity?
>
> Even if the resulting generic operators weren't first class,
> it would still be useful to define 'map' once and be done
> with it.
>

Hm... maybe you have in mind something called "polytypic programming"

Example: pmap :: Regular d => (a->b) -> d a -> d b
    A generalisation to all regular datatypes of the normal map on lists .
Applies a function to all elements in a structure.

Take a look at http://www.cs.chalmers.se/~patrikj/poly/

- Tom

[-- Attachment #2: Type: text/html, Size: 1148 bytes --]

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

end of thread, other threads:[~2006-12-24 11:00 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2006-12-22 11:18 map and fold skaller
2006-12-22 12:12 ` [Caml-list] " Florian Hars
2006-12-23 23:54 ` Andrej Bauer
2006-12-24  3:26   ` skaller
2006-12-24  3:42   ` skaller
2006-12-24 11:00     ` Tom

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).