I'm trying to define an exception in OCaml that accepts a tuple pair of lists as an argument. However, this situation doesn't work?
# exception Foo of string list * string list;;
exception Foo of string list * string list
# let bar = (["a"], ["b"; "c"; "d"]);;
val bar : string list * string list = (["a"], ["b"; "c"; "d"])
# raise(Foo bar);;
Error: The constructor Foo expects 2 argument(s),
but is applied here to 1 argument(s)
However, if I do this, it works
# raise (Foo (["a"], ["b"; "c"; "d"]));;
Exception: Foo (["a"], ["b"; "c"; "d"]).
What's the deal? Thanks!
You're looking at this wrong (though I won't blame you: it's pretty surprising at first). It may seem to you that constructors follow the syntax Name of type
where the type part follows normal type syntax (which lets it contain tuples).
In reality, tuples and constructors follow the exact same syntax: a constructor is merely a tuple with a name in front of it:
tuple/constructor == [name of] type [* type] [* type] ...
So, the *
in a constructor definition are not part of the tuple syntax, they're part of the constructor syntax. You're literally defining a constructor as being this name, followed by N arguments as opposed to this name, followed by an argument which is a tuple.
The reason behind this subtle difference in behavior is one of performance. Right now, tuples and constructors are represented in memory as such:
[TYPE] [POINTER] [POINTER] [POINTER]
This is a fairly compact and efficient representation. If the multiple arguments of a constructor could indeed be accessed as a tuple, this would require the runtime to represent that tuple independently from the constructor (in order for it to be independently addressable) and so it would look like this:
[TYPE] [POINTER]
|
v
[TYPE] [POINTER] [POINTER] [POINTER]
This would use marginally more memory, require twice as many allocations when using a constructor, and reduce the performance of pattern-matching tuples (because of an additional dereference). In order to retain maximum performance, the name of type * type
is represented using the first pattern, and you need to explicitly type name of (type * type)
to cut off the *
from the of
and thus fall back on the second pattern.
Note that both patterns are accessed through the same pattern-matching and construction syntax: name (arg,arg)
. This means that type inference cannot deduce the pattern based on usage. This is no problem for normal constructors, which are always defined in a type definition, but it causes variants (which need no preliminary definition) to automatically fall back on the second version.
Additional reading on the memory representation of types here.
In this respect, OCaml's exception constructors are just like ordinary constructors:
Constr(1,2,3)
is a special syntactic construct in which no triple occurs. On the other hand, a triple occurs in Constr((1,2,3))
. The implementation matches this behavior, with Constr(1,2,3)
being allocated as a single block, and Constr((1,2,3))
as two blocks, one containing a pointer to the other(the triple). In the run-time representation of Constr(1,2,3)
there is no triple to get a pointer to, and if you need one you have to allocate a fresh one.
Note: Constr(((1,2,3)))
is equivalent to Constr((1,2,3))
. In Constr(((1,2,3)))
, the middle parentheses are interpreted as going around the expression (1,2,3)
, and parentheses around an expression are forgotten in the abstract syntax tree.
Foo
is a constructor of an exception with 2 parameters. You'd have to decompose the tuple and pass each part into it.
# exception Foo of string list * string list;;
exception Foo of string list * string list
# let bar = (["a"], ["b"; "c"; "d"]);;
val bar : string list * string list = (["a"], ["b"; "c"; "d"])
# let a, b = bar in raise (Foo (a, b));;
Exception: Foo (["a"], ["b"; "c"; "d"]).
If you wish to use a tuple as the single parameter, you must define the exception using parens and pass the tuple in.
# exception Foo of (string list * string list);;
exception Foo of (string list * string list)
# let bar = (["a"], ["b"; "c"; "d"]);;
val bar : string list * string list = (["a"], ["b"; "c"; "d"])
# raise (Foo bar);;
Exception: Foo (["a"], ["b"; "c"; "d"]).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With