caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* RE: [Caml-list] Please help a newbie
@ 2001-08-02 13:37 Dave Berry
  2001-08-02 16:08 ` Brian Rogoff
  0 siblings, 1 reply; 9+ messages in thread
From: Dave Berry @ 2001-08-02 13:37 UTC (permalink / raw)
  To: ocaml mailing list

For the language designers on the list:  This is an example of why
curried notation is a bad idea.  If the function were written using
tuples, Johann would have been less likely to make this mistake, and if
he had, the compiler could have given a message that was easier to
understand.


-----Original Message-----
> # let rec wys_die_lys l = function
>     [] -> []
>         | h :: t -> wys_dit h ::    wys_die_lys l t;;
> val wys_die_lys : 'a -> string list -> unit list = <fun>
-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr


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

* RE: [Caml-list] Please help a newbie
  2001-08-02 13:37 [Caml-list] Please help a newbie Dave Berry
@ 2001-08-02 16:08 ` Brian Rogoff
  0 siblings, 0 replies; 9+ messages in thread
From: Brian Rogoff @ 2001-08-02 16:08 UTC (permalink / raw)
  To: Dave Berry; +Cc: ocaml mailing list

On Thu, 2 Aug 2001, Dave Berry wrote:
> For the language designers on the list:  This is an example of why
> curried notation is a bad idea.  If the function were written using
> tuples, Johann would have been less likely to make this mistake, and if
> he had, the compiler could have given a message that was easier to
> understand.

This could also be construed as an instance of "function" being a bad
idea, or at least, a little confusing to beginners. It's confusing because 
it takes an implicit argument. If match had been used, I don't think 
this would have happened. In the Revised syntax of CamlP4 function is 
omitted (for other reasons I think). While I stumbled a bit at first with
function, I use it a bit now. 

-- Brian


-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr


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

* Re: [Caml-list] Please help a newbie
  2001-08-03 18:23 ` [Caml-list] Please help a newbie md5i
@ 2001-08-05 21:37   ` John Max Skaller
  0 siblings, 0 replies; 9+ messages in thread
From: John Max Skaller @ 2001-08-05 21:37 UTC (permalink / raw)
  To: md5i; +Cc: Caml-list

md5i@cs.cmu.edu wrote:
> 
> Johann Spies <jspies@maties.sun.ac.za> writes:

>(BTW, in OCaml, the type unit is
> sort of equivilent to C's void.)

	As an aside: in Felix, there are two types,
unit (which has a single value, the empty tuple),
and void, which has no values. The two are
distinct (indeed, dual): unit is the categorical terminal, void
is the categorical initial.

	In Ocaml, the categorical initial also exists,
thanks to polymorphic variants:

	type void = [] 

It is my contention that the type of procedures is wrong
in ocaml precisely because they return unit, when they
_should_ be returning void (that is, nothing).

An example of the kind of thing this typing
permits is:

	let f () = print_endline "Hi" in
	f ( f() )

This works, because the type of f is 

	unit -> unit

instead of what it should be:

	unit -> void

I note that the sequencing operator ; in such a system
can't work correctly with the type

	(;): unit -> unit -> unit

used in Ocaml. It can't be translated to

	(;): void -> void -> void

but has to be translated to

	(;): (unit->void) -> (unit->void) -> (unit->void)

which means the result is never actually executed.
You need 

	(;;): unit->void->void

for that, and call it a stamement: statements can 
be concatenated, there cannot be a 'functional'
way to execute statements in sequence, which provides
a division between expressions and statements.

I'm experimenting with a coherent syntax for this
in Felix (which distinguishes expressions from statements,
and functions from procedures).

-- 
John (Max) Skaller, mailto:skaller@maxtal.com.au 
10/1 Toxteth Rd Glebe NSW 2037 Australia voice: 61-2-9660-0850
New generation programming language Felix  http://felix.sourceforge.net
Literate Programming tool Interscript     
http://Interscript.sourceforge.net
-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr


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

* Re: [Caml-list] Please help a newbie
  2001-08-03 10:58 Johann Spies
@ 2001-08-03 18:23 ` md5i
  2001-08-05 21:37   ` John Max Skaller
  0 siblings, 1 reply; 9+ messages in thread
From: md5i @ 2001-08-03 18:23 UTC (permalink / raw)
  To: Caml-list

Johann Spies <jspies@maties.sun.ac.za> writes:

> Thank you to everyone that replied. I have realized now that it is not
> that easy to start programming in ocaml (or a functional language)
> when you are an amateur and coming from a procedural/imperative
> programming background.

I wouldn't say that it is difficult, so much that there are some new
concepts to be learned.

> Maybe Sylvain summarized my problems with his questions:
> 
>> There are many different things in your code. Answering to some
>> questions might help you:
>> + what is the type of lys?
>> + what is the type of the elements of lys?
> 
> I am not sure about the difference between these two questions.  I
> realised my mistake that lys was a char list and I expected wys_dit to
> print strings.

>From your session:

  # let lys = ['a';'b';'c'];;
  val lys : char list = ['a'; 'b'; 'c']

Based on this we can say that the type of lys is a char list, and the
type of the elements of lys is char.  ('a', 'b', and 'c' are the
elements of lys).

>> + what is the type of wys_dit?
> 
> As I understand it wys_dit is a function.  I always thought of
> functions as using types not necessarily having types.

Even in C, you can think of functions as having a type.  A function's
type encompasses its arguments and its retrn value.  Let's look at
wys_dit:

  # let wys_dit woord = print_string woord;;
  val wys_dit : string -> unit = <fun>

According to the interpreter, the type of wys_dit is string -> unit.
This means that it is a function which takes as an argument a string
and returns a value of type unit.  (BTW, in OCaml, the type unit is
sort of equivilent to C's void.)

>> + how many parameters do you want for wys_die_lys?
> 
> I thought it needed one, originally, but it did not work, so I tried a
> second.  At least the interpreter did not complain.  Another response
> to my question, explained why I did not see an interpreter complaint.
> 
>> + do you see the interpreter answers that it "expects" two
>>   parameters?
> 
> No, I did not see that.  I do not always understand the messages of
> the interpreter.

Okay.  Let's take a look:

  # let rec wys_die_lys l = function
      [] -> []
          | h :: t -> wys_dit h ::    wys_die_lys l t;;
  val wys_die_lys : 'a -> string list -> unit list = <fun>

Okay, the interpreter says that the type of wys_die_lys is 
'a -> string list -> unit list

The simplest explanation of this is that wys_die_lys is a function
that takes two arguments, one of which is of any type ('a), and the
second of which is a list of strings.  It returns a list of units.
Why is this?  Primarily because you are misusing the function
keyword.  This is not surprising; function can be a confusing special
case to the beginner.  More explained below:

>> + can you compare the function f1 and f2 such that:
>> let f1 x = match x with
>> | [] -> print_string "empty list";print_newline()
>> | h::t -> print_string "not empty list";print_newline();;
>> let f2 = function 
>> | [] -> print_string "empty list";print_newline()
>> | h::t -> print_string "not empty list";print_newline();;
>> + how many parameters do they need?
> 
> Maybe this is what I am struggling to understand.  I can not see how
> f2 can work.  What data does it use?

The thing is, function defines a function which takes one argument,
and does pattern matching on it. You don't see the argument above
because it is implicit.  Until you understand this, use the first
pattern above which uses match.

> Related to this is what Benedikt referred to:
> 
>> But the easiest way is:
>> 
>> let wys_die_lys = List.iter print_string;;
> 
> In this function, there is no explicit parameter.  Where is this
> behaviour documented?

Okay, here is where I tell you that something I said above is a white
lie.  A function with a signature of 'a -> string list -> unit list
does not really take two arguments.  It actually takes one.  The type
could be rewritten as:

'a -> (string list -> unit list)

I.e., it is a function which takes an argument of any type, and
returns a function that takes a string list and returns a unit list.
Whew!  Guess what?  You are learning about what is called "Currying".
Let's use a simple example to explain.

  # let add (a, b) = a + b;;
  val add : int * int -> int = <fun>
  # add (1, 2);;
  - : int = 3

Okay.  Here add is a function with the type signature 
  int * int -> int
This means it takes a pair of ints and returns an int.  In FP
parlance, int * int is a touple of two ints.  int * string would be a
touple of an int and a string, and 'a * 'b * 'c would be a touple of 3
items of types 'a, 'b, and 'c.  So, in practice, the function plus
above takes a single argument which just happens to be two ints, and
returns an int.  Okay, lets look at a very similar function:

  # let plus a b = a + b;;
  val plus : int -> int -> int = <fun>
  # plus 1 2;;
  - : int = 3

Okay, here we have a slightly different function.  It has the
following type signature:
  int -> int -> int
As we can see, it seems to take two int arguments and returns an int.
But there is a little more going on here than that.

  # let plusone = plus 1;;
  val plusone : int -> int = <fun>

Okay, what is going on here?  We are calling the plus function, but we
are only calling it with one argument!  How does this work?  Well, in
Caml, all functions really only take one argument. Lets look at the
type signature again, only let's add some parenthesies to make things
clearer: 

int -> (int -> int)

plus is a function which takes an int, and returns a function which
takes an int and returns an int.  Above, when we defined plusone, we
called plus with the int 1, and got back a function which takes and
int and returns an int.  This is sometimes called partial function
application.  Let's try it out.

  # plusone 2;;
  - : int = 3

Tadah!  When you are calling making a function call like plus 1 2,
this is actually interpreted as ((plus 1) 2), i.e., call the function
returned by plus 1 with the argument 2.

Now, let's return to Sylvain's suggestion:

>> let wys_die_lys = List.iter print_string;;

  # List.iter;;
  - : f:('a -> unit) -> 'a list -> unit = <fun>

Okay, the type of List.iter is 
('a -> unit) -> 'a list -> unit
[You can forget about the f:() stuff for now.  These are labels, and
we can discuss those in another message.]  Let's interpret this type.
List.iter takes as an arguemnt a function which takes an argument of
any type ('a), and returns a function which takes an argument of a
list of that type ('a list), and returns unit.  Wow.  That's a bit
complicated.  Now let's look at the type of print_string.

  # print_string;;
  - : string -> unit = <fun>

That was simple enough.  print_string takes an argument of type
string, and returns unit.  Now, if you remember, List.iter takes as
it's argument a function from any type to unit.  Let's use
print_string as the argument to List.iter.

  # let wys_die_lys = List.iter print_string;;
  val wys_die_lys : string list -> unit = <fun>

Now wys_die_lys is a function which takes a string list as an
argument, and returns a unit.  this is exactly what you would have
gotten if you plugged in the signature from print_string into the
signature for List.iter above.  As for what List.iter does, we will
have to refer to the manual.  List.iter takes a function and a list of
items, and will apply that function to each of the items in order.
Hence, wys_die_lys will take a list of strings and call print_string
on each one.

Whew!  My fingers are tired.  I'll let you think about the above
before I try to reply to the rest of your post.  I hope this has
helped a little.

-- 
Michael Welsh Duggan
(md5i@cs.cmu.edu)
-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr


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

* [Caml-list] Please help a newbie
@ 2001-08-02 13:42 Johann Spies
  2001-08-02 12:54 ` Frank Atanassow
                   ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Johann Spies @ 2001-08-02 13:42 UTC (permalink / raw)
  To: ocaml mailing list

I have read a lot of tutorials and examples and am now trying to write
something myself.  My problem will show that I do not understand a
basic thing in functional programming.  I will appreciate some help on
this.

The following illustrates my problem:

------------------
$ ocaml
        Objective Caml version 3.01

# let lys = ['a';'b';'c'];;
val lys : char list = ['a'; 'b'; 'c']
# let wys_dit woord = print_string woord;;
val wys_dit : string -> unit = <fun>
# let rec wys_die_lys l = function
    [] -> []
        | h :: t -> wys_dit h ::    wys_die_lys l t;;
val wys_die_lys : 'a -> string list -> unit list = <fun>
# wys_die_lys lys;;
- : string list -> unit list = <fun>
# wys_die_lys ['x','y','z'];;
- : string list -> unit list = <fun>
------------------------------------------------

Now my question:  I expected 

wys_die_lys lys to print out a b c
and 
wys_die_lys ['x','y','z'] to print x y z

Why did this not happen?


Johann
-- 
Johann Spies          Telefoon: 021-808 4036
Informasietegnologie, Universiteit van Stellenbosch

     "All scripture is given by inspiration of God, and is 
      profitable for doctrine, for reproof, for correction, 
      for instruction in righteousness;"          
                                     II Timothy 3:16 
-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr


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

* Re: [Caml-list] Please help a newbie
  2001-08-02 13:01 ` Francois Thomasset
@ 2001-08-02 13:25   ` Francois Thomasset
  0 siblings, 0 replies; 9+ messages in thread
From: Francois Thomasset @ 2001-08-02 13:25 UTC (permalink / raw)
  To: Francois Thomasset; +Cc: Johann Spies, ocaml mailing list

I was wrong at the end of my message: you don't need begin..end as shown by 
the other answers
FT


-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr


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

* Re: [Caml-list] Please help a newbie
  2001-08-02 13:42 Johann Spies
  2001-08-02 12:54 ` Frank Atanassow
  2001-08-02 13:01 ` Francois Thomasset
@ 2001-08-02 13:05 ` Sven
  2 siblings, 0 replies; 9+ messages in thread
From: Sven @ 2001-08-02 13:05 UTC (permalink / raw)
  To: Johann Spies; +Cc: ocaml mailing list

On Thu, Aug 02, 2001 at 01:42:13PM +0000, Johann Spies wrote:
> I have read a lot of tutorials and examples and am now trying to write
> something myself.  My problem will show that I do not understand a
> basic thing in functional programming.  I will appreciate some help on
> this.
> 
> The following illustrates my problem:
> 
> ------------------
> $ ocaml
>         Objective Caml version 3.01
> 
> # let lys = ['a';'b';'c'];;
> val lys : char list = ['a'; 'b'; 'c']
> # let wys_dit woord = print_string woord;;
> val wys_dit : string -> unit = <fun>
> # let rec wys_die_lys l = function

maybe this should be : 

let rec wys_die_lys  function

>     [] -> []
>         | h :: t -> wys_dit h ::    wys_die_lys l t;;

and here ... wye_die_lys t;;

> val wys_die_lys : 'a -> string list -> unit list = <fun>

here you have a function taking a first argument of type 'a, thorwing it away
and using a second argument.

> # wys_die_lys lys;;
> - : string list -> unit list = <fun>

trye wys_die_lys () lys for example.

> # wys_die_lys ['x','y','z'];;
> - : string list -> unit list = <fun>

Same here.

Hope this helped you ...

Friendly,

Sven Luther
-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr


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

* Re: [Caml-list] Please help a newbie
  2001-08-02 13:42 Johann Spies
  2001-08-02 12:54 ` Frank Atanassow
@ 2001-08-02 13:01 ` Francois Thomasset
  2001-08-02 13:25   ` Francois Thomasset
  2001-08-02 13:05 ` Sven
  2 siblings, 1 reply; 9+ messages in thread
From: Francois Thomasset @ 2001-08-02 13:01 UTC (permalink / raw)
  To: Johann Spies; +Cc: ocaml mailing list

Answer of another newbie: several things are wrong in your piece of code:

1/ 'a' is a char, and print_string works for strings, noted "a"

2/ your wys_die_lys has 2 arguments, as can be seen from the signature
val wys_die_lys : 'a -> string list -> unit list = <fun>
(the system deduced the string list type for the type of the second argument 
from the call to print_string in wys_dit). The 1st argument is named l, but 
you do pattern matching on the 2nd (which has no name). So wys_die_lys lys is 
another function:
wys_die_lys lys;;
- : string list -> unit list = <fun>
If you provide a list of chars as a 2nd argument to wys_die_lys it is rejected 
by the type checking:
wys_die_lys lys lys;;
This expression has type char list but is here used with type string list
Normal: see point 1/ above.

3/ Now if you give a string list as 2nd argument:
wys_die_lys lys ["a";"b";"c"];;
cba- : unit list = [(); (); ()]
The 1st argument lys is just passed around, and in fact ignored; wys_dit does 
its printing job and returns a unit to its caller, hence the list of units 
which is the result.

4/ Here is a definition which gets closer to what you expected:
let wys_dit woord = print_char woord;;
val wys_dit : char -> unit = <fun>
let rec wys_die_lys = function               
    [] -> ()                                   
    | h :: t -> begin wys_dit h ; wys_die_lys t end;;
val wys_die_lys : char list -> unit = <fun>
Now the units returned by calls to wys_dit are discared thanks to the sequence 
operator ` ; '. Note you have to pack the sequence between begin..end in an 
alternative, after the ` '-> '
# wys_die_lys lys;;                                     
abc- : unit = ()

Best Regards


-- 
					François Thomasset.
					INRIA (A3)

Tel: +33 (1) 39-63-54-75
Fax: +33 (1) 39-63-53-30 ou +33 (1) 39-63-59-95
Email: Francois.Thomasset@inria.fr
Smail: INRIA, Rocquencourt, BP 105, 78153 Le Chesnay Cedex, France


-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr


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

* RE: [Caml-list] Please help a newbie
  2001-08-02 13:42 Johann Spies
@ 2001-08-02 12:54 ` Frank Atanassow
  2001-08-02 13:01 ` Francois Thomasset
  2001-08-02 13:05 ` Sven
  2 siblings, 0 replies; 9+ messages in thread
From: Frank Atanassow @ 2001-08-02 12:54 UTC (permalink / raw)
  To: Johann Spies, ocaml mailing list

> # let lys = ['a';'b';'c'];;
> val lys : char list = ['a'; 'b'; 'c']
> # let wys_dit woord = print_string woord;;
> val wys_dit : string -> unit = <fun>
> # let rec wys_die_lys l = function
>     [] -> []
>         | h :: t -> wys_dit h ::    wys_die_lys l t;;
> val wys_die_lys : 'a -> string list -> unit list = <fun>
> # wys_die_lys lys;;
> - : string list -> unit list = <fun>
> # wys_die_lys ['x','y','z'];;
> - : string list -> unit list = <fun>
> ------------------------------------------------
>
> Now my question:  I expected
>
> wys_die_lys lys to print out a b c
> and
> wys_die_lys ['x','y','z'] to print x y z
>
> Why did this not happen?

(Note: I only tested a few of the definitions that appear below. If I made a
mistake somewhere, hopefully you will not be too confused by it.)

As you can see from the reported type,

  wys_die_lys : 'a -> string list -> unit list

wys_die_lys is a "function of two arguments"; if you only pass it the first,
and the result is just another function, which expects a list of strings.
Also, the first argument of wys_die_lys, l, is never used (though it is
passed down recursively). You probably wanted to write:

  let rec wys_die_lys2 = function
      [] -> []
    | h :: t -> wys_dit h :: wys_die_lys2 t;;

You mentioned l on the lefthand side, and then used 'function' on the
righthand side. So the definition expects two arguments. Consider these
three definitions, which all define the same thing:

  let f x y = x+y;;
  let f x = function y -> x+y;;
  let f = function x -> function y -> x+y;;

Another problem is that the list you supply is a list of characters, but
wys_dit is expecting a string. So you should use the appropriate function
print_char:

  let wys_dit2 l = print_char l;;
  let rec wys_die_lys3 = function
      [] -> []
    | h :: t -> wys_dit2 h :: wys_die_lys3 t;;

Normally, you would have gotten a type error to alert you to this problem,
but because l is not used in wys_die_lys, it was assigned the type 'a, which
means you could pass a value of any type in for it. That makes sense,
because if you never use l, it does not matter what type of value it is
bound to.

But even if you use the definition above, it won't give the you result you
expect. Instead, you will get:

  # wys_die_lys3 ['x','y','z'];;
  zyx

The reason is that Ocaml evaluates function arguments from right to left.
The consing operator (::) is a function. Consequently, it will recurse down
the righthand side of the :: until it finds the empty list [], and then only
on its trip back will evaluate the lefthand side, wys_dit2 h.

A solution to this, then, is to use a let-expression, which evaluates the
expressions bound to let-variables before it evaluates the body.

  let rec wys_die_lys4 = function
      [] -> []
    | h :: t -> let x = wys_dit2 in
                x :: wys_die_lys4 t;;

This should work. It will return a list of units in every case, i.e.,
something of this form.

  [(); (); ... ()]

However, you are probably not interested in these sorts of results, so the
more conventional way to define something like this is to throw away the
result of wys_dit, and change the returned value in the first branch (the
base case) from [] to ().

  let rec wys_die_lys5 = function
      [] -> ()
    | h :: t -> let x = wys_dit2 h in
                wys_die_lys5 t;;

In these cases, x is never used (it's always the unit value ()), so you
probably want to use the sequencing operator ';' instead. So:

  let rec wys_die_lys6 = function
      [] -> ()
    | h :: t -> wys_dit2 h; wys_die_lys6 t;;

Lastly, there is no need to define wys_dit2; it does exactly the same thing
as print_char, so you can just write:

  let rec wys_die_lys7 = function
      [] -> ()
    | h :: t -> print_char h; wys_die_lys6 t;;

Incidentally, this definition would be more useful if you parametrized it
with a function doe_dit itself. That way you can perform some particular
action on every element of the list.

  let rec doe_met doe_dit = function
      [] -> ()
    | h :: t -> doe_dit h; doe_met doe_dit t;;

For example,

  doe_met print_char

is equal to the function wys_die_lys7. In fact, doe_met is already defined
in the List library, where it is called List.iter.

Hope that helped.

---
Frank Atanassow, Information & Computing Sciences, Utrecht University
Padualaan 14, PO Box 80.089, 3508TB Utrecht, The Netherlands
Tel +31 (0)30 253-3261 Fax +31 (0)30 251-3791

-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr


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

end of thread, other threads:[~2001-08-05 21:38 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2001-08-02 13:37 [Caml-list] Please help a newbie Dave Berry
2001-08-02 16:08 ` Brian Rogoff
2001-08-02 13:42 Johann Spies
2001-08-02 12:54 ` Frank Atanassow
2001-08-02 13:01 ` Francois Thomasset
2001-08-02 13:25   ` Francois Thomasset
2001-08-02 13:05 ` Sven
2001-08-03 10:58 Johann Spies
2001-08-03 18:23 ` [Caml-list] Please help a newbie md5i
2001-08-05 21:37   ` John Max Skaller

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