Can't get bind operator to work with discriminated union - f#

I'm trying to use the "bind operator" (>>=) in my code.
If I use the operator I get a compile error, if I instead "inline" what the operator is supposed to do, it works.
type TestDI =
private
| A of string list
| B of int list
with
static member (>>=) (x: string list, f: TestDI -> 'a) =
f <| A x
let func (t: TestDI) =
match t with
| A _ -> "a"
| B _ -> "b"
// Expecting a type supporting the operator '>>=' but given a function type.
// You may be missing an argument to a function.
["a"] >>= func
// works
func <| A ["a"]
Obviously I'm missing something, can someone please help?

When you use an operator, F# looks for it in order:
as a let-defined operator;
as a static member-defined operator on one of the two arguments' types. Here, the arguments you are passing to the operator are string list and TestDI -> string, so it won't look at the one you defined on TestDI.
So here the solution would be to let-define it instead:
type TestDI =
private
| A of string list
| B of int list
let (>>=) (x: string list) (f: TestDI -> 'a) =
f <| A x

Related

Deconstructing Discriminated Unions

I have a discriminated-union type of the form
type ParameterName = string
type ParameterValues =
| String of string[]
| Float of float[]
| Int of int[]
type Parameter = Parameter of ParameterName * ParameterValues
I want to pass the ParameterValues part to a function taking generic arguments returning unit, such as
let func1 (name:string) (data:'a) = printfn "%s" name
To deconstruct Parameter I could wrap func1 like this
let func2 (Parameter (name, values)) =
match values with
| String s -> func1 name s
| Float s -> func1 name s
| Int s -> func1 name s
however this is inconvenient if I have to do this for multiple functions. Instead, I would like to define a more flexible wrapper like this:
let func3 (fn: ('a -> 'b -> unit)) (Parameter (name, values)) =
match values with
| String s -> fn name s
| Float s -> fn name s
| Int s -> fn name s
This however fails, as the type of b gets restricted to string[] in the first option of the match expression; consequently the match expression fails with the error Type string does not match type float.
Is this expected? How can I work around this problem?
This is an expected behaviour. The problem is that you cannot directly pass a generic function as an argument to another function in F#. When you define a function as follows:
let func3 (fn: ('a -> 'b -> unit)) (Parameter (name, values)) = (...)
... you are defining a generic function func3 that has two generic parameters and, when those are specified, can be called with a given function and a parameter. This can be written as:
\forall 'a, 'b . (('a -> 'b -> unit) -> Parameter -> unit)
What you would need to do is to make those type parameters not top-level, but make the first parameter itself a generic function. You could write this as:
(\forall 'a, 'b . ('a -> 'b -> unit)) -> Parameter -> unit
This can be clumsily written in F# using interfaces:
type IFunction<'a> =
abstract Invoke<'b> : 'a -> 'b -> unit
let func1 =
{ new IFunction<string> with
member x.Invoke<'b> name (data:'b) = printfn "%s" name }
let func3 (fn: IFunction<string>) (Parameter (name, values)) =
match values with
| String s -> fn.Invoke name s
| Float s -> fn.Invoke name s
| Int s -> fn.Invoke name s
In practice, your function does not really need to be generic, because you are never using the second argument - but you could probably achieve pretty much anything that you can achieve with this interfaces trick just by passing the data as obj and your code would be significantly simpler than this monstrosity!
Following the suggestions by Lee and Tomas, I came up with the following solution:
type Parameter = Parameter of string * obj
let func0 (name:string) (data:obj) = printfn "%s %A" name data
let func1 (fn: string->obj->unit) (Parameter (name, value)) =
fn name value
let p1 = Parameter ("p1", [|"a"; "b"|])
let p2 = Parameter ("p1", [|1.; 2.|])
func1 func0 p1
func1 func0 p2

Can I reprogram op_Equals in F#

I'm doing a fun project in F#, which is a DSL for Camel.Net.
At some point, I want to check conditions. But the conditions entered by the programmer should evaluate to an object tree. So I want the experssion "a = b" evaluate to "SomeType.Equals(a,b)"
Is that even possible in F#?
I have this:
type Macro =
| Header of string
| XPath of string
| Const of string
| Func of (Message -> string)
with
static member (=) (l:Macro, r:Macro) = Equals(l,r)
static member (=) (l:Macro, r:string) = Equals(l,Const(r))
and Comparison =
| Equals of Macro * Macro
Now everything in "Macro" will work as "Macro.Func" - with "Func"; a function is executed with "Message" as input param and will output the string. So the Equals(a,b) will evaluate to a string comparison during runtime.
But this code has a problem. Operator (=) does compile (it has a warning), but it can't be used as I would like.
This does not compile in the fsi:
let c1 = Header("property") = "somevalue"
I did read another question about this topic, and a bit more.
It does not answer my question.
[<NoEquality; NoComparison>] - completely shuts off the (=) operator.
[<CustomEquality; CustomComparison>] - wants you to implement an (=) operator which returns bool.
Is it even possible in F# what I want? And assuming that I can find a way, does match x with still work?
Sure, I did this reimplement to the operator in terms of System.IEquatable<T> for performance reasons:
#nowarn "86" // F# doesn't like it when you do this
[<AutoOpen>]
module FastEquals =
let inline eq<'a when 'a :> System.IEquatable<'a>> (x:'a) (y:'a) = x.Equals y
let inline (=) x y = eq x y
let inline (<>) x y = not (eq x y)
Just an example, you'll need to adapt for your own purposes.
Thanks to Asik's answer above, in combination with a reread of this post:
This works in the fsi:
type Message = class end
type Macro =
| Header of string
| XPath of string
| Const of string
| Func of (Message -> string)
type Comparison =
| Equals of Macro * Macro
type Operators = Operation with
static member CompareEquals (Operation, l:Macro, r:Macro) = Equals(l,r)
static member CompareEquals (Operation, l:Macro, r:string) = Equals(l,Const(r))
#nowarn "0086" "0064"
let inline (=) (l:'N) (r:'M) = ((^T or ^N or ^M) : (static member CompareEquals : ^T * ^N * ^M -> _) (Operation, l, r))
let c1 = Header("property1") = Header("property2")
let c2 = Header("property") = "somevalue"
Note that it does not work when the static "CompareEquals" methods are located in the "Macro" type.
If you look at the signature of op_Equals:
val inline ( = ) :
l: ^N -> r: ^M -> 'a
when (Operators or ^N or ^M) : (static member CompareEquals : Operators * ^N * ^M -> 'a)
That is a really weird syntax. I don't understand the part after "when". It works, that counts.

Error FS0001, this function was expected to have type byref<int>

I have a made a simple function that wraps common F# functions of signature 'a -> 'b -> 'c option to more "C# compliant" function as : 'a -> b -> byref<'c> -> bool. But somehow when I try to wrap such a method in a class I am getting error FS0001 and I can't locate the error.
Code below
open System
open System.Runtime.InteropServices
// Given a function, f: 'a -> 'b -> 'c option returns
// A new function g: 'a -> 'b -> byref<'c> -> bool
let wrapOptionF f a b (g:byref<'c>) =
match f a b with
| Some v ->
do g <- v
true
| None ->
false
let tryDivide (a:int) (b:int) =
match Math.DivRem(a,b) with
| v, 0 -> Some v
| _ -> None
type TryDivideWrapper() =
static member TryDivide(a, b, [<Out>]cRef:byref<int>) : bool =
let f = wrapOptionF tryDivide a b
f cRef
The offending line is f cRef.
This post contains a more in-depth explanation, but in short you can replace your final type definition with the following:
type TryDivideWrapper() =
static member TryDivide(a, b, [<Out>]cRef:byref<int>) : bool =
wrapOptionF tryDivide a b &cRef
The reason for this is that your wrapOptionF takes a byref parameter. However, byref<int> isn't really a type like int or int ref - it's just an indication to the compiler that your parameter should be passed by reference (like out in C#). Once inside your function, however, what you have is a regular int.
Edit: Note that Intellisense will show cRef as having type byRef<int>. However, if you bind another variable to cRef, you'll see that the type you get is a regular int. You can put this line inside TryDivide and then hover your mouse over a to see it:
let a = cRef
Using the & operator tells the compiler that you're passing cRef into f by reference - which is exactly what f needs - making the type system happy. I've tested this using a C# project, and TryDivideWrapper.TryDivide(a, b, out c) works as expected. Add in #MarkSeemann's tryDivide and you should be good to go.
I'm not exactly sure I understand the reason, but this seems to work:
type TryDivideWrapper() =
static member TryDivide(a, b, [<Out>]cRef:byref<int>) : bool =
wrapOptionF tryDivide a b &cRef
BTW, the OP tryDivide implementation throws an exception on tryDivide 1 0. Here's an alternative implementation that works:
let tryDivide (a:int) (b:int) =
if b = 0
then None
else Some (a / b)
FSI:
> tryDivide 1 0;;
val it : int option = None
> tryDivide 10 5;;
val it : int option = Some 2

How to interpret a function signature

I am still confused on how to read function signatures.
The Option.map signature is the following:
/// map f inp evaluates to match inp with None -> None | Some x -> Some (f x).
/// mapping: A function to apply to the option value.
/// option: The input option.
val map : mapping:('T -> 'U) -> option:'T option -> 'U option
However, I have no clue what that signature means.
I read it as the following:
There's a function called map that takes a function as an input that we'll call "mapping" and it will yield a result that is also a function that we'll call "option".
Mapping Parameter:
mapping:('T -> 'U)
The function that we pass in as input takes Titanium (i.e. 'T) as the input and yields Uranium (i.e. 'U) as output.
The Option returned
option:'T option -> 'U option
We'll call the output of the map function "option".
Thus, this "option" that is returned from executing the map function is also a function as referenced above. It's takes a Titanium option and yields a Uranium option.
Example:
type String20 = String20 of string
type Name = { First:String20
Last:String20
Suffix:String20 option }
let tryCreateName (first:string) (last:string) (suffix:string option) =
let isValid = [first; last]
|> List.forall (fun x -> x.Length > 2 && x.Length <= 20)
if isValid then
Some { First = String20(first);
Last = String20(last);
Suffix = Option.map String20 suffix }
else None
How does the following expression map:
Option.map String20 suffix
Based on the expression above, where is the "returned function" of Titanium option -> Uranium option?
First off take a look at Option.map<'T,'U> Function (F#) and notice
The expression map f inp evaluates to match inp with None -> None |
Some x -> Some (f x).
So lets convert this comment to working code. First map is a method of the type Option, but to make it easier we will make it a function outside of a type and to avoid conflicts with other map functions we will give it the name OptionMap.
let OptionMap = f inp =
match inp with
| None -> None
| Some x -> Some (f x)
Now to get the signature of this function just send it to F# Interactive
val OptionMap : f:('a -> 'b) -> inp:'a option -> 'b option
and to make the types obvious we will type the parameters of the function.
let optionMap (f : ('a -> 'b)) (inp : 'a option) : 'b option =
match inp with
| None -> None
| Some x -> Some (f x)
Now to test this we can use
let (test : String20 option) = optionMap String20 (Some("something"))
printfn "test: %A" test
// test: Some (String20 "something")
So what happened in OptionMap that allowed this to work
If we add some print statements let sees what happens
let optionMap (f : ('a -> 'b)) (inp : 'a option) : 'b option =
printfn "f: %A" f
printfn "inp: %A" inp
match inp with
| None -> None
| Some x ->
let result = Some (f x)
printfn "result: %A" result
result
we get
f: <fun:test#63>
inp: Some "something"
result: Some (String20 "something")
we see that f is a function which is String20
So how can String20 be a function?
If we send String20 to F# Interactive it gives.
> String20;;
val it : arg0:string -> String20 = <fun:clo#4>
So String20 is a function that takes a string and returns a type of String20. That smells of a constructor.
Let's test that out in F# interactive.
> let test1 = String20 "something";;
val test1 : String20 = String20 "something"
Sure enough, String20 is a constructor, but we didn't specifically create a constructor as is done in the Object-Oriented world.
You have to think of a type, even a discriminated union as having constructors. The constructors are not specifically written but do exist. So String20 is a constructor that takes one value, a string, which is a function with the correct type signature for the Option.map function.
I gave the answer a lot more detail so that one can learn a process on how to break down problems and look at the inner workings as a tool to solving these kinds of problems.
To learn more about the lower level details of how functional programming works one needs to understand lambda calculus. The best way I know to learn it is to read An Introduction To Functional Programming Through Lambda Calculus by Greg Michaelson and look at the info in the lambda calculus tag.
Also if you like the book, you should buy a copy instead of using the free version.
There are two ways of looking at it.
let f x y = x + y
// val f: x:int -> y:int -> int
One way is to say that function f takes two parameters, x of type int and y of type int, and returns an int. So I can supply two arguments and get the result:
let a = f 4 5
// val a: int = 9
The other way is that the function takes one parameter, x of type int, and returns another function, which takes one parameter, y of type int, and returns an int. So I can supply one argument and get a function as result:
let b = f 4
// val b: int -> int
Mathematically, it's always the second way - all functions are one-parameter functions. This notion is very convenient for programming with higher-order functions.
But the first way is usually more understandable to humans, so you will often see functions discussed as if they take multiple parameters.
String20 is the case constructor for the String20 Discriminated Union case. It's a function with the type string -> String20. So string takes the place of 'T, and String20 takes the place of 'U in the mapping you supply to Option.map.
Generally, if you have a function T1 -> T2 -> ... and you apply it to one parameter, you get a function T2 -> .... In the case of Option.map, T1 is itself a function, but that is of no consequence to the way the arguments are applied.
I find it confusing to call a function "option", a string "Titanium" and a type called String20 "Uranium", so I'll stick with type names.
You ask where the "returned function" that maps a string option to a String20 option is in the expression Option.map String20 suffix. It is simply
Option.map String20
Since String20 constructs a String20 from a string, Option.map String20 is a function that maps a string to a String20 if it is present, and to None otherwise.
If you write Option.map String20 suffix, this function is applied to suffix, which would be a string option. The result of this expression is a String20 option.

Resolve operator conflicts on similar generic types

I'm using a third party library (Sauve.IO) which defines the standard bind operator >>= :
val inline (>>=) : first:('T -> Async<'U option>) -> second:('U -> Async<'V option>) -> input:'T -> Async<'V option>
I also have an internal library that would also like to define the operator over the type signature
Async<Response<'a>> -> ('a -> Async<Response<'b>>) -> Async<Response<'b>>
What is the cleanest way to use these two functions in the same namespace/module without running afoul of the restriction on overloading inline functions ?
How about renaming Suave's operator to some other infix operator?
let (>=>) = Suave.(>>=)
Judging from the signature, it looks like it is not bind anyway,
but actually Kleisli composition.
You may be able to leverage F#'s statically resolved ad-hoc polymorphism: hide the qualified operator invocation behind a separate, overloaded operator; then define yet another, inlined operator for actual use.
type Foo = Foo with
static member ($) (_ : Foo, first : _ -> Async<_ option>) =
fun second value -> Module1.(>>=) first second value
static member ($) (_ : Foo, arg1 : Async<Response<_>>) =
Module2.(>>=) arg1
let inline (>>=) arg1 arg2 = (Foo $ arg1) arg2
Do both operators have the same name (>>=) ?
If so I assume that Suave's operator came with it's own module or namespace?
In this case you can qualify their operator appending their module/namespace name like this Suave.(>>=) but in this case you have to call it as usual function. Here is simplified example:
module Suave =
let inline (>>=) a b = a + 2 * b
module Mine =
open Suave
let (>>=) a b = a - 3 * b
let r1 = Suave.(>>=) 1 3
let r2 = 1 >>= 3
Result:
val r1 : int = 7
val r2 : int = -8

Resources