Expected compiler behaviour confusion

Hi there. I expect the following not to compile, but yet it does. Am I missing something here?

let fn1 = str => Js.log(str)
let fn2 = () => Js.log("Empty")

let fn3 = opt => {
  switch opt {
  | None => fn1
  | Some(_) => fn2
  }
}

let fn4 = fn3(None)
let fn5 = fn3(Some("hi"))

fn4() // expect this not to compile as is a "call" of fn1 ?
fn5() 

Thanks in advance

Js.log is typed as 'a => unit and calling it without any parameters is the same as passing unit as the 'a (at least that’s my understanding of things).

The following also compiles:

let fn1 = str => Js.log(str)

fn1()

Thanks Kevin,

Yes this breaks.

let fn1 = str => Js.log(str)
let fn2 = () => Js.log("Empty")

let fn3 = opt => {
  switch opt {
  | None => fn1
  | Some(_) => fn2
  }
}

let fn4 = fn3(None)
let fn5 = fn3(Some("hi"))

fn4("Hi") // This breaks
fn5()

This is the expected behavior. The inferred type for fn3 is (option<'a>, unit) => unit. Therefore, fn4 and fn5 are both inferred as type unit => unit. Passing a string to either one is a type error.

Note that if we don’t rely on the compiler’s type inference, and force fn1 to accept a string, then the error is different:

let fn1 = (str: string) => Js.log(str)
let fn2 = () => Js.log("Empty")

let fn3 = opt => {
  switch opt {
  | None => fn1
  | Some(_) => fn2 // ERROR
                   // This has type: unit => unit
                   //  Somewhere wanted: string => unit
  }
}
1 Like