Too impatient outputting optional ocaml function argument

Consider this code:

let myFun
  ?(f: ('a -> int) = (fun x -> x))
  (x: 'a)
    : int =
  f x

      

It looks like I can't call any other argument except int

. When I try with this code:

let usage = myFun ~f:String.length "abcdef"

      

Ocaml emits this error message:

Error: This expression has type string -> int
       but an expression was expected of type int -> int
       Type string is not compatible with type int 

      

It looks like the output will think 'a = int

because of the default argument. Is this a limitation of the language or is there a way how to write this so that it compiles?

+3


source to share


4 answers


This issue is a limitation due to the way the optionals argument is implemented.

Essentially, a function with an additional argument is expanded during the type-checking phase using the standard option type. For example your function:

let f ?(conv=(fun x -> x)) x = f x

      

becomes more or less

let f opt_conv =
let conv =
  match opt_conv with
  | None -> fun x -> x
  | Some f -> f in
 fun x -> conv x

      

Hence, since it opt_conv

has a type 'a->'a

in the branch None

, f must have for the type ?conv:('a->'a) -> 'a -> 'a

.

Looking at the extended function, the problem arises because the branch Some

and None

must be of a different type in order to get the desired functionality.

For quiet puzzles that have different types in different pattern matching branches, this is a sign that GADTs can bring a potential solution: you can define an extended option type as



type ('default,'generic) optional =
  | Default: ('default,'default) optional
  | Custom: 'a -> ('default,'a) optional

      

then you can rewrite your function as

let f: type a. (int -> int, a -> int) optional -> a -> int =
fun conv x ->
  match conv with
  | Default -> x
  | Custom f -> f x

      

which leads to the expected behavior:

f Default "hi"

gives a type error whereas it f (Custom int_of_string) "2"

returns 2.

However, without the optional syntactic syntax mechanism, this is not very useful.

No, it's perfectly possible to extend OCaml to use the optional

GADT-laded type. However, this can easily lead to dire type errors, and the corresponding increase in complexity doesn't make for a very attractive extension.

+5


source


The notation ?(f: ('a -> int)

does not mean that the parameter f

has a type 'a -> int

, it is a type constraint that tells the type inference algorithm what f

should be a function that returns a type value int

. 'a

here simply means, "I don't care, do it yourself." It doesn't generalize your type variable. Thus, given your constraint, the compiler reports that (1) the type f

is equal int -> int

(because it is the only input that satisfies your default), (2) the type is x

also int

because you constrained this yourself (it will be inferred for of the same type due to type f

.

Also, the type system is right here, forbidding anything other than int

as a parameter x

. Consider the following example:



myFun "hello"

      

According to your desire, it should be valid, but what should OCaml do in this case?

+5


source


When you write

let myFun
  ?(f: ('a -> int) = (fun x -> x))
  (x: 'a)
    : int =
  f x

      

You are asserting a generic type, but that is a mistake.

As you can see, when you interpret the function:

let myFun ?(f: ('a -> int) = (fun x -> x)) x = f x;;

val myFun : ?f:(int -> int) -> int -> int = <fun>`

      

Focus on this: f: ('a -> int) = (fun x -> x)

Here f

, the default is a take x

and return function, x

which means that the argument must be the same type as the return value. You wrote that it f

has a type 'a -> int

, then the return type int

, so the type of the parameter is also int

(and not 'a

).

You can just write: f: ('a -> 'a) = (fun x -> x)

and everything will work fine.

let myFun ?(f: ('a -> 'a) = (fun x -> x)) x = f x;;

val myFun : ?f:('a -> 'a) -> 'a -> 'a = <fun>

      


Update

If you want your function to always return int

, you need to change the default function. For example, you can write:

let myFun ?(f: ('a -> int) = (fun _ -> 0)) x = f x;;

val myFun : ?f:('a -> int) -> 'a -> 'a = <fun>

      

+1


source


Type inference occurs at the "let" level, so this kind of polymorphism is not possible.

The only way is without an optional argument

let myFun f x : int = f x;;

myFun id 1;;
myFun int_of_string "2";;
myFun String.length "abc";;

      

Where id

isfun x -> x

0


source







All Articles