Reputation: 997
I want to write a sample function that takes a list of any type and returns the head element only if the list is a list of reals.
Otherwise, the function should give the error message
. . . E r r o r : operator and operand don ’ t . . .
datatype typeR = Real of real
fun head(xs) =
case xs of
[] => raise List.Empty |
Real (x::_) => x:real ;
Upvotes: 0
Views: 383
Reputation: 16105
First some feedback:
This function is prone to failure since it only accepts a margin of its possible input. Write total functions when you can.
raise Empty
would not be descriptive of the situation where the list is non-empty but the first element doesn't contain a real. Another exception should be raised then.
The following requirement is highly suspicious.
Otherwise, the function should give the error message
. . . E r r o r : operator and operand don ’ t . . .
It suggests that you don't differentiate between run-time exceptions and compile-time errors. This is symptomatic for a programming experience with dynamically typed languages where errors and exceptions are all handled after the program is started.
Then some ideas:
Lists in Standard ML are homogeneous, which means they can only be of one type at a time determined in the type-checking phase of compilation. There are a few ways you can deal with that and achieve more dynamic typing;
Using algebraic data types [wiki.haskell.org]:
datatype dynamic = R of real
| I of int
| B of bool
| S of string
fun headR [] = raise Empty
| headR (R r :: _) = r
| headR _ = raise Domain
Then headR
does not actually accept any type. It accepts exactly one, dynamic
, that can hold any fixed amount of value constructors with parameters of arbitrary type. This mimics heterogeneous lists:
val foos = [R 42.0, I 10, B true, S "Hello, World!"]
Using exceptions [informallyabstract.blogspot.com]:
datatype dynamic = Dynamic of exn
exception IsReal of real
fun wrap_real r = Dynamic (IsReal r)
fun unwrap_real (Dynamic ex) = raise ex handle IsReal r => r
fun headR (d::_) = unwrap_real
| headR [] = raise Empty
Then headR
likewise only accepts one type, dynamic
, and fails with whatever exception that was used instead of the real
wrapper. The difference between using exceptions and a regular datatype
definition is that exceptions can be extended with new constructors later:
exception IsString of string
fun wrap_string r = Dynamic (IsString r)
fun unwrap_string (Dynamic ex) = raise ex handle IsString r => r
val foos = [wrap_real 42.0, wrap_string "This didn't exist before."]
Neither are preferable because they introduce an unnecessary risk of run-time failure.
Upvotes: 0
Reputation: 66371
It's not entirely clear what you're after – you can't write a function that takes "any type" and then examines the parameter type.
(Writing head(xs)
, without a type annotation, does not make head
a function that takes any type. Its type is inferred.)
If you want a function typeR list -> real
, your main mistake was writing Real (x::_)
where you should have written (Real x) :: _
.
That is,
fun head(xs) =
case xs of
[] => raise List.Empty
| (Real x)::_ => x
or, more idiomatically,
fun head [] = raise List.Empty
| head (Real x :: _) = x
Upvotes: 2
Reputation: 36
(fn x:real list => hd x ) [ 4 ,5 ];
out> error: Type error in function application
(fn x:real list => hd x ) [ 4.0 ,5.0 ];
out> it = 4.0: real
Upvotes: 1