Question

type Alignment =
     | Horizontal
     | Vertical

let getMainAttr = function
                  | Horizontal -> fst
                  | Vertical -> snd

let check alignment =
    let mainAttr = getMainAttr alignment
    mainAttr (2,3) |> ignore
    mainAttr (2.0, 3.0) // error

val getMainAttr : _arg1:Alignment -> ('a * 'a -> 'a)
mainAttr : (int * int -> int) // because of the value restriction

it seems the only way to make it generic is to make it explicit, e.g. let mainAttr x = getMainAttr alignment x

However, thus it no longer utilize closure, so that each time mainAttr is called alignment has to be checked.

Is there any way to only check alignment once as well as being generic?

Was it helpful?

Solution

As @Daniel describes, you're hitting the value restriction limitation which disallows creating generic values that are result of some F# computation (even if the value is a function). You can find more information about this in other SO questions and there is also an article about advanced points. The reason for this restriction is that generic values can introduce a loophole in the type safety.

In your example, you do not really need to worry, because executing the getMainAttr function repeatedly is not going to add that much overhead. If the function did a more complex computation, you could return an interface with a generic method (instead of a simple function):

/// Interface with as single generic function that selects element of a pair
type PairSelector = 
  abstract Invoke<'T> : 'T * 'T -> 'T

// Two possible implementations of the interface 
let first = { new PairSelector with member x.Invoke(a, b) = a }
let second = { new PairSelector with member x.Invoke(a, b) = b }

// Return a non-generic `PairSelector` value instead of a generic function
let getMainAttr = function
                  | Horizontal -> first
                  | Vertical -> second

// Now we can get `PairSelector` value and call it with different type arguments    
let check alignment =
    let mainAttr = getMainAttr alignment
    mainAttr.Invoke (2,3) |> ignore
    mainAttr.Invoke (2.0, 3.0) 

OTHER TIPS

As it is, mainAttr is a function value. Values cannot be generic. The solution, as you discovered, is to make it a "true" function by making the parameter explicit.

Regarding your last question, considering that alignment is a value and not a function, do you really care how many times it will be evaluated? I'm not sure, but I don't think currying will even affect this.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top