What is the leading ? is F# type signature? - f#

When I hover certain methods that come from C# libraries in F#, some of the parameters start with a ?. I'm unfamiliar with this syntax. Does this mean nullable?
Here is an example from hovering the SetStatus method on the Activity class where the description parameter is preceded by a ?.

The optional parameters in F# are declared with question marks in the declaration of the function:
https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/parameters-and-arguments#optional-parameters
What is interesting that the optionality expressed this way turns them to actual options of the base type, however there are differences: The options are 'active' only within the scope of the member method, and you have to pass the underlying type variable (or nothing in this case)
type Test() =
member this.fn (?i : int) =
match i with
| Some x -> -x
| None -> 0
let t = new Test()
let r1 = t.fn()
printfn "%d" r1
let r2 = t.fn(4)
printfn "%d" r2
If you declare your member method as int option, you can't omit the parameter despite identical implementation of the method otherwise:
type Test() =
member this.fn (i : int option) =
match i with
| Some x -> -x
| None -> 0
let t = new Test()
let r1 = t.fn()
printfn "%d" r1
let r2 = t.fn(4)
printfn "%d" r2
Here's the output:
/home/jdoodle.fs(9,14): error FS0001: This expression was expected to have type
'int option'
but here has type
'unit'
/home/jdoodle.fs(12,15): error FS0001: This expression was expected to have type
'int option'
but here has type
'int'
In order to make it working, you have to pass int option to the method:
let r1 = t.fn(None)
printfn "%d" r1
let r2 = t.fn(Some 4)
printfn "%d" r

Related

How to print function argument name that was used to call this function in F#

How can I print argument function name that was used to call this function?
open System
open System.Threading.Tasks
let logger (f: ('a -> Task<'b>)) : ('a -> Task<'b>) =
printfn "Hey: %O" (nameof f) // I would like to print "myFunc", not "f"
f
let myFunc x = task {return x }
let d = (logger myFunc) 3
You could use the ReflectedDefinition(true) attribute, which automatically quotes the argument of a method call and gives you both the value (to use at runtime) and the code quotation from which you can (if the format is right) extract the name. This only seems to work with method calls though:
type Logger =
static member log([<ReflectedDefinition(true)>]f: Expr<('a -> Task<'b>)>) : ('a -> Task<'b>) =
match f with
| Patterns.WithValue(v, _, Patterns.Lambda(_, Patterns.Call(_, mi, _))) ->
printfn "Hello %s!" mi.Name
unbox v
| _ -> failwith "Wrong format"
let myFunc x = task {return x }
let d = (Logger.log myFunc) 3
The design and motivation of this is discussed in the F# 4.0 Speclet: Auto-Quotation of Arguments at Method Calls

Creating a random fractional number in F#

I was trying to create a random fractional number as follows:
type Fraction=
{
num: int
den: int
}
let makeRandomFraction i =
let n= fun i -> System.Random(i).Next(-50, 51)
let d= fun i -> System.Random(i*3).Next(-50, 51)
{num=n; den=d}
but I am getting an error:
error FS0001: This expression was expected to have type
'int'
but here has type
'int -> int'
Could you please explain the error and the correct way of doing the required.
The error is saying that you're passing a function (of type int -> int) where an int is expected. You can see this more clearly in something like:
let add a b = a + b
let inc x = x + 1
inc add
// ^^^
// This expression was expected to have type 'int' but has type 'int -> int -> int'
The solution here is just to take out the fun i -> parts. That's the (other) syntax for lambdas, but since your i variable is already in scope there's no need to create a function around it.
Out of curiosity, do you have any requirements on what range and distribution of fractions do you want your function to generate? The way the code is currently written - by composing two random numbers in a range -50 .. 50, you will get a distribution with most numbers being close to zero.
Here is a simple histogram built using the XPlot F# library:
open XPlot.GoogleCharts
type Fraction=
{ num: int
den: int }
let makeRandomFraction i =
let n = System.Random(i).Next(-50, 51)
let d = System.Random(i*3).Next(-50, 51)
{num=n; den=d}
[ for i in 0 .. 100000 -> let f = makeRandomFraction i in float f.num / float f.den ]
|> Seq.filter (System.Double.IsInfinity >> not)
|> Seq.countBy (fun f -> int f)
|> Chart.Column

Simple closure function

I have following code
let f2 x:int =
fun s:string ->
match x with
| x when x > 0 -> printfn "%s" s
| _ -> printfn "%s" "Please give me a number that is greater than 0"
And the compiler complain:
Unexpected symbol ':' in lambda expression. Expected '->' or other token.
What am I doing wrong?
You have to put parentheses around your type annotations:
let f2 (x : int) =
fun (s : string) ->
match x with
| x when x > 0 -> printfn "%s" s
| _ -> printfn "%s" "Please give me a number that is greater than 0"
Also be aware if you omit the parentheses around x like in your example, this would mean the function f2 returns an int, not constrain the type of x to be int.
Update for the comment:
Why if I omit the parentheses around x, this would mean the function f2 returns an int?
Because that is how you specify the return type of functions.
What would be this in C#:
ReturnTypeOfFunction functionName(TypeOfParam1 firstParam, TypeOfParam2 secondParam) { ... }
would look like this in F#:
let functionName (firstParam : TypeOfParam1) (secondParam : TypeOfParam2) : ReturnTypeOfFunction =
// Function implementation that returns object of type ReturnTypeOfFunction
A more detailed explanation can be found on MSDN.
You have two instances of the same problem. When defining a function, putting :*type* at the end of the signature indicates that the function returns that type. In this case you're indicating that you have a function f2 which takes a parameter and returns an int. To fix it you need to put parenthesis around the annotation. That syntax doesn't work in a lambda, so you simply get a compile error instead.
Or let the compiler infer the types. Try this:
let f2 x =
fun s ->
match x with
| x when x > 0 -> printfn "%s" s
| _ -> printfn "%s" "Please give me a number that is greater than 0"

How to flatten input of mixed 'T and seq<'T> into single seq<'T>

I need a function that could take an arbitrary number of arguments, each could be either of type 'T or seq<'T>. Inside the function I need to process it as a single seq<'T> with all inputs combined in the same order as they sere supplied.
The obvious way was to have something like:
module Test =
let flatten ([<ParamArray>] args) =
let flat = seq {
for a in args do
match box a with
| :? int as x -> yield x
| :? seq<int> as sq ->
for s in sq do
yield s
| _ -> failwith "wrong input type"
}
flat // this should be seq<int>
but I cannot make it work in FSI even with the simplest case
let fl = Test.flatten 1;;
----------------------^
...: error FS0001: The type 'int' is not compatible with the type 'seq<'a>'
What is wrong here and how to get it work as needed? Probably this could be done in some completely different way?
From msdn :
In F#, parameter arrays can only be defined in methods. They cannot be
used in standalone functions or functions that are defined in
modules.
So instead of a module, declare a type with a static method.
open System
type Test() =
static member flatten ([<ParamArray>] args: obj[]) =
let flat = seq {
for a in args do
match box a with
| :? int as x -> yield x
| :? seq<int> as sq ->
for s in sq do
yield s
| _ -> failwith "wrong input type"
}
flat
If you have other let bindings you can still declare a module with the same name.
Also note that in the second guard of the match you can avoid the for loop by doing:
| :? seq<int> as sq -> yield! sq
And box is not required.

For loops in 2D arrays giving incorrect types in F#

I suspect that I am missing something very obvious here but this doesn't work:
let t = Array2D.create 1 1 1.0
for x in t do printfn "%f" x;;
It fails with
error FS0001: The type 'obj' is not compatible with any of the types float,float32,decimal, arising from the use of a printf-style format string
Interestingly using printf "%A" or "%O" prints the expected values which suggests to me that the problem is with the type inference
The corresponding code for a 1D array works fine
let t = Array.create 1 1.0
for x in t do printfn "%f" x;;
For reference this is on version 2.0 (both interactive and compiler) running on the latest mono
In .NET, a 1D array implicitly implements IList, which means it also implements (by inheritance) IEnumerable<T>. So, when you run:
let t = Array.create 1 1.0
for x in t do printfn "%f" x;;
the F# compiler emits code which gets an implementation of IEnumerable<T> (seq<T> in F#) from t, then iterates over it. Since it's able to get an IEnumerable<T> from the array, x will have type T.
On the other hand, multi-dimensional arrays (2d, 3d, etc.) only implement IEnumerable (not IEnumerable<T>) so the F# compiler infers the type of x as System.Object (or obj, in F#).
There are two solutions for what you want:
Cast each individual value within the loop, before printing it:
for x in t do printfn "%f" (x :?> float);;
Or, use Seq.cast to create and iterate over a strongly-typed enumerator:
for x in (Seq.cast<float> t) do printfn "%f" x;;
As Jack pointed out, this is a problem. One easy solution is:
let t = Array2D.create 2 2 1.0
t |> Array2D.iter (printfn "%f");;
And if you really like the for .. in .. do syntax:
type Array2DForLoopBuilder() =
member __.Zero() = ()
member __.For(a, f) = Array2D.iter f a
member __.Run e = e
let a2dfor = Array2DForLoopBuilder()
let t = Array2D.init 2 2 (fun a b -> float a + float b)
a2dfor { for x in t do printfn "%f" x }

Resources