For all the progress I've made in F#, I still get lost in various of the constructor and deconstructor syntax.
I'm running a recursive simulation. One of the parameters is a function for the stopping condition. I have various possible stopping conditions to choose from. I make them all have the same signature. So I decide it would be nice, and educational, to lock down these functions to a custom type--so that not just any function that happens to match the signature can be sent:
type StoppingCondition = | StoppingCondition of (ReturnType -> ReturnType -> int -> float -> bool)
I think I'm doing this right, from tutorials, having a type name and an identical constructor name (confusing...), for a single case discriminated union. But now I can't figure out how to apply this type to an actual function:
let Condition1 lastRet nextRet i fl =
true
How do I make Condition1 be of type StoppingCondition? I bet it's trivial. But I've tried putting StoppingCondition as the first, second or last term after let, with and without parens and colons. And everything is an error.
Thanks for the patience found here.
EDIT:
I'll try to synthesize what I lean from the four answers (as of this moment), all good:
Trying to mimic this pattern:
s : string = "abc"
I was trying to write:
type StoppingCondition = | StoppingCondition of (ReturnType -> ReturnType -> int -> float -> bool)
let condition1 lastRet nextRet i fl : StoppingCondition = // BAD
//wrong for a type alias, totally wrong for a union constructor
true
//or
let condition1 : StoppingCondition lastRet nextRet i fl = // BAD again
true
or other insertions of : Stopping Condition
(trying to prefix it, in the way that constructors go, in that one line).
Now I see that to get what I was getting at I would have to do:
type StoppingCondition = | StoppingCondition of (ReturnType -> ReturnType -> int -> float -> bool)
let conditionFunc1 lastRet nextRet i fl = //...
true
let stoppingCondition1 = StoppingCondition conditionFunc1
//or
let stoppingCondition2 = StoppingCondition <| (func lastRet nextRet i fl -> false)
//and there's another variation or 2 below
And what I didn't appreciate as a big negative to this approach is how a union type is different from a type alias. A type alias of string
admits of the string functions when declared--it really is a string and does "string things. A single case discriminated union of string
--is not a string any more. To have it do "string things" you have to unwrap it. (Or write versions of those functions into your type (which might be wrappers of the string functions).) Likewise a type alias of my function accepts those parameters. A DU of my function is just a wrapper and doesn't take arguments. So this doesn't work with discriminated union:
let x = stoppingCondition1 ret1 ret2 2 3.0 // BAD
//"stoppingCondition1 is not a function and cannot be applied"
And there's not enough value in my case here to work around the wrapper. But a type alias works:
type StoppingAlias = ReturnType -> ReturnType -> int -> float -> bool
let stoppingCondition:StoppingAlias = fun prevRet nextRet i x -> true
let b = stoppingCondition ret1 ret2 10 1.0 // b = true
I may not have everything straight in what I just said, but I think I'm a lot closer.
Edit 2:
Side note. My question is about defining the type of a function. And it compares using a type alias and a union type. As I worked at trying to do these, I also learned this about using a type alias:
This works (from: https://fsharpforfunandprofit.com/posts/defining-functions/ ):
type Adder = decimal -> decimal -> decimal
let f1 : Adder = (fun x y -> x + y)
//or
let f2 : decimal -> decimal -> decimal = fun x y -> x + y
but these are wrong:
let (f2 : Adder) x y = x + y // bad
let (f3 x y) : (decimal -> decimal -> decimal) = x + y // bad
let (f3 : (decimal -> decimal -> decimal)) x y = x + y // bad
And some discussion on this whole issue: F# Type declaration possible ala Haskell?
(And also, yeah, "assigning a type" isn't the right thing to say either.)
As has been said, you have to construct an instance of a StoppingCondition
from an appropriate function, for example:
let Condition1 = StoppingCondition (fun _ _ _ _ -> true)`
One nice way to do this without weird indentation or extra parentheses is a backward pipe:
let Condition1 = StoppingCondition <| fun lastRet nextRet i fl ->
// Add function code here
The signature might be long enough to justify a record type instead of four curried parameters. It's a question of style and how it'll be used; the result may look like this:
type MyInput =
{ LastReturn : ReturnType
NextReturn : ReturnType
MyInt : int
MyFloat : float }
type StopCondition = StopCondition of (MyInput -> bool)
let impossibleCondition = StopCondition (fun _ -> false)
let moreComplicatedCondition = StopCondition <| fun inp ->
inp.MyInt < int (round inp.MyFloat)
To call the function inside a StopCondition
, unwrap it with a pattern:
let testStopCondition (StopCondition c) input = c input