* [Caml-list] Design advice
@ 2002-09-27 16:47 Dan Schmidt
2002-09-28 10:48 ` John Prevost
` (3 more replies)
0 siblings, 4 replies; 11+ messages in thread
From: Dan Schmidt @ 2002-09-27 16:47 UTC (permalink / raw)
To: caml-list
I've been using ocaml for only a couple of months now, and it's not
yet clear to me how to do some things most idiomatically in it.
One has to do with making the equivalent of an enum in C. Say I'm
implementing a deck of cards for a card game. There are four suits,
and none of them have any special properties (this isn't a game like
bridge where different suits are treated differently). I could do
type suit = Spades | Hearts | Diamonds | Clubs
but this seems a little heavyweight; it seems weird to perform pattern
matching on values that really have no semantic importance other than
the fact that they are different from each other. It's not like I'm
going to have any code that does one thing when given the 3 of Spades
and another thing when given the 3 of Hearts. The other issue is that
it is mildly annoying to, for example, write a compare function to be
used inside a struct that implements the OrderedType signature (e.g.,
if I want to have a Set of cards).
The alternative is to just use an int, but then it is theoretically
possible that I could pass in ints outside the valid range and get
run-time errors. The idea of guaranteeing that a suit is never
invalid appeals to me.
The same sort of issue comes up with representing the two players of
the game. I could use an int and just raise exceptions everywhere if
it turns out to be not 0 or 1. Or I could make a
type player = Player_one | Player_two
type, but now I have to convert this to an integer whenever I want to
use the player number as an index. Which sort of approach usually
ends up being less of a hassle in the end? Or does it depend on the
particular application? Or is there yet a third solution which is
better than either of these? I've been vacillating, and right now I
am inclined to go with the variants at the expense of having to
indirect every once in a while.
Finally, is there any type in the library that functions like an
immutable array? I would like to have an indexable bunch of values
but use it in a purely functional way. I could always just use
Arrays and copy them before updating, but if there's already an
idiomatic type to use I'd prefer to use that.
Thanks,
Dan
--
http://www.dfan.org
-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Caml-list] Design advice
2002-09-27 16:47 [Caml-list] Design advice Dan Schmidt
@ 2002-09-28 10:48 ` John Prevost
2002-09-28 10:55 ` Chris Hecker
` (2 subsequent siblings)
3 siblings, 0 replies; 11+ messages in thread
From: John Prevost @ 2002-09-28 10:48 UTC (permalink / raw)
To: Dan Schmidt; +Cc: caml-list
>>>>> "ds" == Dan Schmidt <dfan@dfan.org> writes:
ds> {...} it seems weird to perform
ds> pattern matching on values that really have no semantic
ds> importance other than the fact that they are different from
ds> each other. It's not like I'm going to have any code that
ds> does one thing when given the 3 of Spades and another thing
ds> when given the 3 of Hearts. The other issue is that it is
ds> mildly annoying to, for example, write a compare function to
ds> be used inside a struct that implements the OrderedType
ds> signature (e.g., if I want to have a Set of cards).
Well, note that in the type you've defined, the only thing in the type
is the different suits. That is the *only* information there. I
don't see why it seems unreasonable to pattern match here.
Note that the built-in comparison operations work just fine on types
defined this way.
ds> {... similar angst about Player_One and Player_Two ...}
In this case, it only makes sense in games with precisely two players.
For example: Go, or Chess. If you think you might be using an array
that you want to index into, you may very well be thinking of a game
with unbounded numbers of players--or a library for unbounded numbers
of players. (For example, Bridge has only four players, but a more
general library for hands of cards might support an arbitrary number.)
In the case of a set number of players, you might use a tuple instead
of an array, since you know the precise size, and you know which
player goes with which item. As an example:
type player = North | South | East | West
type state = { n_hand : hand, s_hand : hand, e_hand : hand, w_hand : hand }
let get_hand p st = match p with
| North -> st.n_hand
| South -> st.s_hand
| East -> st.e_hand
| West -> st.w_hand
Is this a little heavy? Well, possibly. But it's not unreasonable,
and it does at the very least restrict possible "unsafe" operations to
certain sections of code. You might, for example, do this instead of
the above:
type player = (* same *)
type state = hand array
let player_to_index = function
| North -> 0
| South -> 1
| East -> 2
| West -> 3
let get_hand p st = st.(player_to_index p)
And don't export player_to_index or the structure of the state type.
Now the "unsafe" region of your code is just in the library that
contains the above. Anything that uses it manipulates things purely
in terms of the bounded type, and you only have to verify that
indexing works right in the above library.
When in doubt, export the safest interface possible and then ensure
that your module's internals are correct. That way you have at least
ensured that users of your module cannot feed you bad arguments.
ds> Finally, is there any type in the library that functions like
ds> an immutable array? I would like to have an indexable bunch
ds> of values but use it in a purely functional way. I could
ds> always just use Arrays and copy them before updating, but if
ds> there's already an idiomatic type to use I'd prefer to use
ds> that.
There is none in the base O'Caml. But this might be more efficient
than copying all the time:
http://www.ai.univie.ac.at/~markus/home/ocaml_sources.html
Take a look in 4.1.6 (Okasaki's Purely Functional Datastructures in
OCaml), chp9.ml, and look at the random access lists.
-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Caml-list] Design advice
2002-09-27 16:47 [Caml-list] Design advice Dan Schmidt
2002-09-28 10:48 ` John Prevost
@ 2002-09-28 10:55 ` Chris Hecker
2002-09-28 19:02 ` William Lovas
2002-09-29 12:27 ` Lauri Alanko
2002-10-01 11:37 ` Xavier Leroy
3 siblings, 1 reply; 11+ messages in thread
From: Chris Hecker @ 2002-09-28 10:55 UTC (permalink / raw)
To: caml-list
I usually use variants, but it's not like I've been programming caml for
much longer than you have.
>type, but now I have to convert this to an integer whenever I want to
>use the player number as an index.
Note that there is a correspondence between variant constructors with no
arguments and integers
(http://caml.inria.fr/ocaml/htmlman/manual032.html#htoc212), so:
type suit = Spades | Hearts | Diamonds | Clubs
let suit_to_int (s : suit) =
assert (Obj.is_int (Obj.repr s));
((Obj.magic s) : int)
The assert will catch it if you add variables to one of the constructors
and then call this. This uses magic, which is bad, but it alleviates you
having to type the variant constructors again and possibly make an error in
the assocation with the int, which is good. Pick your poison.
Chris
-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Caml-list] Design advice
2002-09-28 10:55 ` Chris Hecker
@ 2002-09-28 19:02 ` William Lovas
2002-09-28 22:01 ` John Gerard Malecki
2002-09-28 22:46 ` Chris Hecker
0 siblings, 2 replies; 11+ messages in thread
From: William Lovas @ 2002-09-28 19:02 UTC (permalink / raw)
To: caml-list
On Sat, Sep 28, 2002 at 03:55:43AM -0700, Chris Hecker wrote:
> >type, but now I have to convert this to an integer whenever I want to
> >use the player number as an index.
>
> type suit = Spades | Hearts | Diamonds | Clubs
>
> let suit_to_int (s : suit) =
> assert (Obj.is_int (Obj.repr s));
> ((Obj.magic s) : int)
>
> The assert will catch it if you add variables to one of the constructors
> and then call this. This uses magic, which is bad, but it alleviates you
> having to type the variant constructors again and possibly make an error in
> the assocation with the int, which is good. Pick your poison.
Wouldn't it be safer to just do something like:
let suit_to_int = function
| Spades -> 0
| Hearts -> 1
| Diamonds -> 2
| Clubs -> 3
? You only have to type it once, and if you change the constructors
around, it won't even compile. Plus it gives you control over which
int a suit corresponds to -- i doubt you can get any sort of guarantee
if you use Obj.magic.
William
-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Caml-list] Design advice
2002-09-28 19:02 ` William Lovas
@ 2002-09-28 22:01 ` John Gerard Malecki
2002-09-28 23:03 ` Chris Hecker
2002-09-30 15:35 ` Kontra, Gergely
2002-09-28 22:46 ` Chris Hecker
1 sibling, 2 replies; 11+ messages in thread
From: John Gerard Malecki @ 2002-09-28 22:01 UTC (permalink / raw)
To: caml-list
This thread reminds me to ask if are there any guarantees for ordering
of variant types? Although the implementation indicates that with
type card = Number of int | Jack | Queen | King | Ace
Jack < Queen and Queen < King it also says that Ace < Number 0. I can
see what is going on with the implementation. I'm curious if there
are any ordering guarantees that I can take advantage of? Since the
documentation is silent on this point I doubt it.
Oh, It does seems as if tuples, arrays and lists are always compared
"from left to right". This can be handy when sorting multi-
dimensional data. This seems like a "more natural" ordering than for
variants but, once again, can this ordering be guaranteed for all
ocaml programs?
-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Caml-list] Design advice
2002-09-28 19:02 ` William Lovas
2002-09-28 22:01 ` John Gerard Malecki
@ 2002-09-28 22:46 ` Chris Hecker
1 sibling, 0 replies; 11+ messages in thread
From: Chris Hecker @ 2002-09-28 22:46 UTC (permalink / raw)
To: William Lovas, caml-list
> let suit_to_int = function
> | Spades -> 0
> | Hearts -> 1
> | Diamonds -> 2
> | Clubs -> 3
>? You only have to type it once, and if you change the constructors
>around, it won't even compile.
As I mentioned in my mail,
let suit_to_int = function
| Spades -> 0
| Hearts -> 1
| Diamonds -> 1
| Clubs -> 3
is a bug that the compiler can't find. That's why I said it was a
tradeoff. Also, having to type the constructors twice in the mli and the
ml is already a huge pain the ass for maintenance and refactoring in my
opinion, and having to do it a third time is completely lame. But yes,
magic is magic, so that's why I didn't say it was absolutely the right
thing. In reality, a ton of C code depends on this and it is documented to
work like this in the manual, so it's probably safe, but I don't like
fencing in the compiler folks any more than necessary. Hence, "choose your
poison".
Chris
-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Caml-list] Design advice
2002-09-28 22:01 ` John Gerard Malecki
@ 2002-09-28 23:03 ` Chris Hecker
2002-09-30 15:35 ` Kontra, Gergely
1 sibling, 0 replies; 11+ messages in thread
From: Chris Hecker @ 2002-09-28 23:03 UTC (permalink / raw)
To: John Gerard Malecki, caml-list
> type card = Number of int | Jack | Queen | King | Ace
On a related note, for Xavier et al., why wasn't Number's field 0 assigned
to the same counter as the int of the non-argument constructors? In other
words, why isn't there a single incrementing int id from left to right,
regardless of arguments? That would have made algorithmic manipulations on
variants easier, because then you just have:
let card_to_int (c : card) : int =
let r = Obj.repr c in
if Obj.is_int r then Obj.magic c else Obj.obj (Obj.field r 0);;
I don't think it's possible to write card_to_int the way the compiler
currently works. If there was a card_cardinality (!) function then you
could do that + field 0 and the argument constructors would start at the
end (still not as nice as if they were in the source code order, but better
than nothing). Maybe you can write that with camlp4 (of course, with
camlp4 you can probably just write the longhand match-with form, but if
you're doing this a lot I'd assume match-with would be slower than just
fetching the field).
Chris
-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Caml-list] Design advice
2002-09-27 16:47 [Caml-list] Design advice Dan Schmidt
2002-09-28 10:48 ` John Prevost
2002-09-28 10:55 ` Chris Hecker
@ 2002-09-29 12:27 ` Lauri Alanko
2002-09-30 16:03 ` Alessandro Baretta
2002-10-01 11:37 ` Xavier Leroy
3 siblings, 1 reply; 11+ messages in thread
From: Lauri Alanko @ 2002-09-29 12:27 UTC (permalink / raw)
To: caml-list
This doesn't help you much, but you may be interested to know that in
Haskell this wouldn't be much of a problem. You would do simply:
data Suit = Spades | Hearts | Diamonds | Clubs
deriving (Eq, Ord, Enum, Ix)
Here "Eq" and "Ord" mean that the compiler generates equality comparison
and ordering functions for the datatype. This is basically what ocaml's
(=) and compare -functions do.
"Enum" means that the compiler generates a bidirectional mapping between
your data type and a subset of natural numbers. This is useful if you
eg. need to communicate Suit values via an external interface (files,
sockets, whatnot).
Finally, "Ix" means that the compiler generates some functionality that
is required for a data type to be usable as an array index. And this is
probably the feature that you seem to need. In Haskell, you could have
an array whose _index_ type is Suit. With such an array, there's no fear
of overflowing, because there simply _aren't_ any index values of the
proper type that weren't included in the domain of the array.
Of course you can get the same safety in ocaml, too, but it just
requires some more work on your part.
This isn't an unconditional plug for Haskell, mind you. Both Haskell and
ocaml are fine languages, and each has great features that the other
lacks.
Lauri Alanko
la@iki.fi
-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Caml-list] Design advice
2002-09-28 22:01 ` John Gerard Malecki
2002-09-28 23:03 ` Chris Hecker
@ 2002-09-30 15:35 ` Kontra, Gergely
1 sibling, 0 replies; 11+ messages in thread
From: Kontra, Gergely @ 2002-09-30 15:35 UTC (permalink / raw)
To: John Gerard Malecki; +Cc: caml-list
> type card = Number of int | Jack | Queen | King | Ace
Another interesting question is how to fence the Number of int
construct?
Gergo
+-[Kontra, Gergely @ Budapest University of Technology and Economics]-+
| Email: kgergely@mcl.hu, kgergely@turul.eet.bme.hu |
| URL: turul.eet.bme.hu/~kgergely Mobile: (+36 20) 356 9656 |
+-------"Olyan langesz vagyok, hogy poroltoval kellene jarnom!"-------+
.
Magyar php mirror es magyar php dokumentacio: http://hu.php.net
-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Caml-list] Design advice
2002-09-29 12:27 ` Lauri Alanko
@ 2002-09-30 16:03 ` Alessandro Baretta
0 siblings, 0 replies; 11+ messages in thread
From: Alessandro Baretta @ 2002-09-30 16:03 UTC (permalink / raw)
To: caml-list; +Cc: Lauri Alanko, Dan Schmidt
Lauri Alanko wrote:
> This doesn't help you much, but you may be interested to know that in
> Haskell this wouldn't be much of a problem. You would do simply:
>
> data Suit = Spades | Hearts | Diamonds | Clubs
> deriving (Eq, Ord, Enum, Ix)
>
> ...
> Finally, "Ix" means that the compiler generates some functionality that
> is required for a data type to be usable as an array index. And this is
> probably the feature that you seem to need. In Haskell, you could have
> an array whose _index_ type is Suit. With such an array, there's no fear
> of overflowing, because there simply _aren't_ any index values of the
> proper type that weren't included in the domain of the array.
>
> Of course you can get the same safety in ocaml, too, but it just
> requires some more work on your part.
>
> This isn't an unconditional plug for Haskell, mind you. Both Haskell and
> ocaml are fine languages, and each has great features that the other
> lacks.
>
>
> Lauri Alanko
> la@iki.fi
type tagged_type = Tag1 of foo | Tag2 of bar | ...
class ['a] tagged_array size =
object
val map = Hashtbl.create size
method set index value = Hashtbl.remove map index;
Hashtbl.add map index value
method get index = Hashtbl.find map index
(* any other methods you might need *)
end
It's not really an array, but it's almost as fast and works
very much like one. I think this approach might solve most
of Dan's problems.
Alex
-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Caml-list] Design advice
2002-09-27 16:47 [Caml-list] Design advice Dan Schmidt
` (2 preceding siblings ...)
2002-09-29 12:27 ` Lauri Alanko
@ 2002-10-01 11:37 ` Xavier Leroy
3 siblings, 0 replies; 11+ messages in thread
From: Xavier Leroy @ 2002-10-01 11:37 UTC (permalink / raw)
To: Dan Schmidt; +Cc: caml-list
Dan Schmidt wrote:
> One has to do with making the equivalent of an enum in C. Say I'm
> implementing a deck of cards for a card game. There are four suits,
> and none of them have any special properties (this isn't a game like
> bridge where different suits are treated differently). I could do
> type suit = Spades | Hearts | Diamonds | Clubs
> but this seems a little heavyweight; it seems weird to perform pattern
> matching on values that really have no semantic importance other than
> the fact that they are different from each other. It's not like I'm
> going to have any code that does one thing when given the 3 of Spades
> and another thing when given the 3 of Hearts. The other issue is that
> it is mildly annoying to, for example, write a compare function to be
> used inside a struct that implements the OrderedType signature (e.g.,
> if I want to have a Set of cards).
As others mentioned, the generic comparison functions (=, <, compare,
etc) work just fine on datatypes, and provide you with a suitable
ordering function for sets or maps.
> Finally, is there any type in the library that functions like an
> immutable array? I would like to have an indexable bunch of values
> but use it in a purely functional way.
You can use maps (module Map from the standard library), using
integers as keys.
> I could always just use
> Arrays and copy them before updating, but if there's already an
> idiomatic type to use I'd prefer to use that.
Actually, there is a better way: "version arrays". The idea is to go
ahead and update in place an array, but record the overwritten array
element in a separate data structure. Thus, you can access the
latest version of the array in constant time, and earlier versions a
bit more slowly. I can't point you to an actual implementation, but
no doubt others on this list can.
John Malecki wrote:
> This thread reminds me to ask if are there any guarantees for ordering
> of variant types? Although the implementation indicates that with
> type card = Number of int | Jack | Queen | King | Ace
> Jack < Queen and Queen < King it also says that Ace < Number 0. I can
> see what is going on with the implementation. I'm curious if there
> are any ordering guarantees that I can take advantage of? Since the
> documentation is silent on this point I doubt it.
> Oh, It does seems as if tuples, arrays and lists are always compared
> "from left to right". This can be handy when sorting multi-
> dimensional data. This seems like a "more natural" ordering than for
> variants but, once again, can this ordering be guaranteed for all
> ocaml programs?
The current implementation works exactly as you described. I'm
reluctant to specify the ordering of variant and product types in more
details, as it depends very much on the data representations chosen by
the compiler, which might change one day (?). It is however very likely
that enumerated datatypes (all constructors are constant) will always
be ordered left-to-right, and that tuples and arrays will always be
ordered lexicographically left-to-right.
Chris Hecker wrote:
> type card = Number of int | Jack | Queen | King | Ace
> On a related note, for Xavier et al., why wasn't Number's field 0 assigned
> to the same counter as the int of the non-argument constructors? In other
> words, why isn't there a single incrementing int id from left to right,
> regardless of arguments?
Caml Light worked as you suggest (only one "counter" for constant and
non-constant constructors). The main reason for having two different
"counters" in OCaml is that integer tags for non-constant constructors
must be less than 246 (i.e. fit in 8 bits, with a few reserved
values). Hence, in Caml Light, no datatype could have more than 246
constructors, while in OCaml a datatype can have arbitrarily many
constant constructors, it's only the non-constant constructors that
are limited in number.
- Xavier Leroy
-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2002-10-01 11:37 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2002-09-27 16:47 [Caml-list] Design advice Dan Schmidt
2002-09-28 10:48 ` John Prevost
2002-09-28 10:55 ` Chris Hecker
2002-09-28 19:02 ` William Lovas
2002-09-28 22:01 ` John Gerard Malecki
2002-09-28 23:03 ` Chris Hecker
2002-09-30 15:35 ` Kontra, Gergely
2002-09-28 22:46 ` Chris Hecker
2002-09-29 12:27 ` Lauri Alanko
2002-09-30 16:03 ` Alessandro Baretta
2002-10-01 11:37 ` Xavier Leroy
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox