joshua
joshua

Reputation: 997

ML how to check type of list and give error massage?

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

Answers (3)

sshine
sshine

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

molbdnilo
molbdnilo

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

HighProfile2018
HighProfile2018

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

Related Questions