Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# assign (custom) type to a function

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.)

like image 668
RomnieEE Avatar asked Dec 18 '22 15:12

RomnieEE


1 Answers

You don't "make it be of type" StoppingCondition. You declare a value of type StoppingCondition and pass Condition1 as the parameter of the DU case constructor:

let stop = StoppingCondition Condition1

That means, however, that every time you want to access the function contained in your single DU case, you have to pattern match over it in some way; that can become annoying.

You say you don't want just any functions that fulfill the signature to be valid as stopping conditions; however, it seems to be specific enough to avoid "accidentally" passing in an "inappropriate" function - with that, you could do something simpler - define StoppingCondition as a type alias for your specific function type:

type StoppingCondition = ReturnType -> ReturnType -> int -> float -> bool

Now you can use StoppingCondition everywhere you need to specify the type, and the actual values you pass/return can be any functions that fulfill the signature ReturnType -> ReturnType -> int -> float -> bool.

like image 192
TeaDrivenDev Avatar answered Dec 21 '22 03:12

TeaDrivenDev