I am fairly new to f#, but I want to know if it is possible to make a function that accepts multiple types of variables.
let add x y = x + y
let integer = add 1 2
let word = add "He" "llo"
Once a function use a type of variable it cannot accept another one.
You need to read about statically resolved type parameters and inline functions. It allows to create functions which may take any type that supports operation and/or have member. So your add function should be defined this way:
let inline add x y = x + y
Don't overuse inlined functions because their code inlined in call site and may increase assembly size, but may increase performance (test each case, don't make predictions!). Also inlined function are supported only by F# compiler and may not work with other languages (important when designing libraries).
Example of SRTP magic:
let inline (|Parsed|_|) (str: string) =
let mutable value = Unchecked.defaultof<_>
let parsed = ( ^a : (static member TryParse : string * byref< ^a> -> bool) (str, &value))
if parsed then
Some value
else
None
match "123.3" with
| Parsed 123 -> printfn "int 123"
| Parsed 123.4m -> printfn "decimal 123.4"
| Parsed 123.3 -> printfn "double 123.3"
// | Parsed "123.3" -> printfn "string 123.3" // compile error because string don't have TryParse static member
| s -> printfn "unmatched %s" s
Related
I'm trying to go from:
sprintf "%3.1f" myNumber
to:
sprintf myFormatter myNumber
which is not possible
I have a situation where number precision depends on some settings, so I would like to be able to create my own formatter string.
I know it can be done with String.Format, but I am curious if there is a F# way with sprintf, or ksprinf; can it be done?
Simple answer
EDIT: Diego Esmerio on F# Slack showed me a simpler way that I honestly never thought of while working out the answer below. The trick is to use PrintfFormat directly, like as follows.
// Credit: Diego. This
let formatPrec precision =
PrintfFormat<float -> string,unit,string,string>(sprintf "%%1.%if" precision)
let x = 15.234
let a = sprintf (formatPrec 0) x
let b = sprintf (formatPrec 1) x
let c = sprintf (formatPrec 3) x
Output:
val formatPrec : precision:int -> PrintfFormat<(float -> string),unit,string,string>
val x : float = 15.234
val a : string = "15"
val b : string = "15.2"
val c : string = "15.234"
This approach is arguably much simpler than the Expr-based approach below. For both approaches, be careful with the formatting string, as it will compile just fine, but break at runtime if it is invalid.
Original answer (complex)
This isn't trivial to do, because functions like sprintf and printfn are compile-time special-case functions that turn your string-argument into a function (in this case of type float -> string).
There are some things you can do with kprintf, but it won't allow the formatting-argument to become a dynamic value, since the compiler still wants to type-check that.
However, using quotations we can build such function ourselves. The easy way is to create quotation from your expression and to change the parts we need to change.
The starting point is this:
> <# sprintf "%3.1f" #>
val it : Expr<(float -> string)> =
Let (clo1,
Call (None, PrintFormatToString,
[Coerce (NewObject (PrintfFormat`5, Value ("%3.1f")), PrintfFormat`4)]),
Lambda (arg10, Application (clo1, arg10)))
...
That may look like a whole lot of mess, but since we only need to change one tiny bit, we can do this rather simply:
open Microsoft.FSharp.Quotations // part of F#
open Microsoft.FSharp.Quotations.Patterns // part of F#
open FSharp.Quotations.Evaluator // NuGet package (with same name)
// this is the function that in turn will create a function dynamically
let withFormat format =
let expr =
match <# sprintf "%3.1f" #> with
| Let(var, expr1, expr2) ->
match expr1 with
| Call(None, methodInfo, [Coerce(NewObject(ctor, [Value _]), mprintFormat)]) ->
Expr.Let(var, Expr.Call(methodInfo, [Expr.Coerce(Expr.NewObject(ctor, [Expr.Value format]), mprintFormat)]), expr2)
| _ -> failwith "oops" // won't happen
| _ -> failwith "oops" // won't happen
expr.CompileUntyped() :?> (float -> string)
To use this, we can now simply do this:
> withFormat "%1.2f" 123.4567899112233445566;;
val it : string = "123.46"
> withFormat "%1.5f" 123.4567899112233445566;;
val it : string = "123.45679"
> withFormat "%1.12f" 123.4567899112233445566;;
val it : string = "123.456789911223"
Or like this:
> let format = "%0.4ef";;
val format : string = "%0.4ef"
> withFormat format 123.4567899112233445566;;
val it : string = "1.2346e+002f"
It doesn't matter whether the format string is now a fixed string during compile time. However, if this is used in performance sensitive area, you may want to cache the resulting functions, as recompiling an expression tree is moderately expensive.
I'm messing around in F# and tried to write a function that can take an int list or a string list. I have written a function that is logically generic, in that I can modify nothing but the type of the argument and it will run with both types of list. But I cannot generically define it to take both.
Here is my function, without type annotation:
let contains5 xs =
List.map int xs
|> List.contains 5
When I try to annotate the function to take a generic list, I receive a warning FS0064: the construct causes the code to be less generic than indicated by the type annotations. In theory I shouldn't need to annotate this to be generic, but I tried anyway.
I can compile this in two separate files, one with
let stringtest = contains5 ["1";"2";"3";"4"]
and another with
let inttest = contains5 [1;2;3;4;5]
In each of these files, compilation succeeds. Alternately, I can send the function definition and one of the tests to the interpreter, and type inference proceeds just fine. If I try to compile, or send to the interpreter, the function definition and both tests, I receive error FS0001: This expression was expected to have type string, but here has type int.
Am I misunderstanding how typing should work? I have a function whose code can handle a list of ints or a list of strings. I can successfully test it with either. But I can't use it in a program that handles both?
You are running into value restrictions on the automatic generalization of the type inference system as outlined here
Specifically,
Case 4: Adding type parameters.
The solution is to make your function generic rather than just making its parameters generic.
let inline contains5< ^T when ^T : (static member op_Explicit: ^T -> int) > (xs : ^T list) =
List.map int xs
|> List.contains 5
You have to make the function inline because you have to use a statically resolved type parameter, and you have to use a statically resolved type parameter in order to use member constraints to specify that the type must be convertible to an int. As outlined here
You can use inline to prevent the function from being fixed to a particular type.
In FSI, the interactive REPL:
> open System;;
> let inline contains5 xs = List.map int xs |> List.contains 5;;
val inline contains5 :
xs: ^a list -> bool when ^a : (static member op_Explicit : ^a -> int)
> [1;2;3] |> contains5;;
val it : bool = false
> ["1";"2";"5"] |> contains5;;
val it : bool = true
Note that the signature of contains5 has a generic element to it. There's more about inline functions here.
This is already answered correctly above, so I just wanted to chime in with why I think it's a good thing that F# appears to makes this difficult / forces us to lose type safety. Personally I don't see these as logically equivalent:
let inline contains5 xs = List.map int xs |> List.contains 5
let stringTest = ["5.00"; "five"; "5"; "-5"; "5,"]
let intTest = [1;2;3;4;5]
contains5 stringTest // OUTPUT: System.FormatException: Input string was not in a correct format.
contains5 intTest // OUTPUT: true
When inlined, the compiler would create two logically distinct versions of the function. When performed on the list<int> we get a boolean result. When performed on a list<string> we get a boolean result or an exception. I like that F# nudges me towards acknowledging this.
let maybeInt i =
match Int32.TryParse i with
| true,successfullyParsedInteger -> Some successfullyParsedInteger
| _ -> None
let contains5 xs =
match box xs with
| :? list<int> as ixs ->
ixs |> List.contains 5 |> Ok
| :? list<string> as sxs ->
let successList = sxs |> List.map maybeInt |> List.choose id
Ok (successList |> List.contains 5)
| _ ->
Error "Error - this function expects a list<int> or a list<string> but was passed something else."
let stringTest = ["5.00"; "five"; "5"; "-5"; "5,"]
let intTest = [1;2;3;4;5]
let result1 = contains5 stringTest // OUTPUT: Ok true
let result2 = contains5 intTest // OUTPUT: Ok true
Forces me to ask if some of the values in the string list cannot be parsed, should I drop out and fail, or should I just try and look for any match on any successful parse results?.
My approach above is horrible. I'd split the function that operates on the strings from the one that operates on the integers. I think your question was academic rather than a real use case though, so I hope I haven't gone off on too much of a tangent here!
Disclaimer: I'm a beginner, don't trust anything I say.
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.
How pattern-matching by type of argument works in F#?
For example I'm trying to write simple program which would calculate square root if number provided or return it's argument otherwise.
open System
let my_sqrt x =
match x with
| :? float as f -> sqrt f
| _ -> x
printfn "Enter x"
let x = Console.ReadLine()
printfn "For x = %A result is %A" x (my_sqrt x)
Console.ReadLine()
I get this error:
error FS0008: This runtime coercion or type test from type
'a
to
float
involves an indeterminate type based on information prior
to this program point. Runtime type tests are not allowed
on some types. Further type annotations are needed.
Since sqrt works with float I check for float type, but guess there could be better solution - like check if input is number (in general) and if so, cast it to float?
The problem here is that the type of x is actually a string. Adding that it comes from Console.ReadLine, what kind of information is stored in that string is only possible to determine at runtime. This means that you can't use neither pattern matching, nor pattern matching with coercion here.
But you can use Active Patterns. As what actual data is stored in x is only known at runtime, you have to parse the string and see what is contains.
So suppose you are expecting a float, but you can't be sure since user can input whatever they want. We are going to try and parse our string:
let my_sqrt x =
let success, v = System.Single.TryParse x // the float in F# is represented by System.Single in .NET
if success then sqrt v
else x
But this won't compile:
This expression was expected to have type float32 but here has type string
The problem is that the compiler inferred the function to return a float32, based on the expression sqrt (System.Single.Parse(x)). But then if the x doesn't parse to float, we intend to just return it, and as x is a string we have an inconsistency here.
To fix this, we will have to convert the result of sqrt to a string:
let my_sqrt x =
let success, v = System.Single.TryParse x
if success then (sqrt v).ToString()
else x
Ok, this should work, but it doesn't use pattern matching. So let's define our "active" pattern, since we can't use regular pattern matching here:
let (|Float|_|) input =
match System.Single.TryParse input with
| true, v -> Some v
| _ -> None
Basically, this pattern will match only if the input can be correctly parsed as a floating point literal. Here's how it can be used in your initial function implementation:
let my_sqrt' x =
match x with
| Float f -> (sqrt f).ToString()
| _ -> x
This looks a lot like your function, but note that I still had to add the .ToString() bit.
Hope this helps.
Just quoting the one and only Scott Wlaschin's 'F# for fun and profit' site:
Matching on subtypes You can match on subtypes, using the :? operator,
which gives you a crude polymorphism:
let x = new Object()
let y =
match x with
| :? System.Int32 ->
printfn "matched an int"
| :? System.DateTime ->
printfn "matched a datetime"
| _ ->
printfn "another type"
This only works to find subclasses of a parent class (in this case,
Object). The overall type of the expression has the parent class as
input.
Note that in some cases, you may need to “box” the value.
let detectType v =
match v with
| :? int -> printfn "this is an int"
| _ -> printfn "something else"
// error FS0008: This runtime coercion or type test from type 'a to int
// involves an indeterminate type based on information prior to this program point.
// Runtime type tests are not allowed on some types. Further type annotations are needed.
The message tells you the problem: “runtime type tests are not allowed
on some types”. The answer is to “box” the value which forces it into
a reference type, and then you can type check it:
let detectTypeBoxed v =
match box v with // used "box v"
| :? int -> printfn "this is an int"
| _ -> printfn "something else"
//test
detectTypeBoxed 1
detectTypeBoxed 3.14
In my opinion, matching and dispatching on types is a code smell, just
as it is in object-oriented programming. It is occasionally necessary,
but used carelessly is an indication of poor design.
In a good object oriented design, the correct approach would be to use
polymorphism to replace the subtype tests, along with techniques such
as double dispatch. So if you are doing this kind of OO in F#, you
should probably use those same techniques.
I have a function that takes a parameter of type object and needs to downcast it to an option<obj>.
member s.Bind(x : obj, rest) =
let x = x :?> Option<obj>
If I pass (for example) an Option<string> as x, the last line throws the exception: Unable to cast object of type 'Microsoft.FSharp.Core.FSharpOption'1[System.String]' to type 'Microsoft.FSharp.Core.FSharpOption'1[System.Object]'.
Or, if I try a type test:
member s.Bind(x : obj, rest) =
match x with
| :? option<obj> as x1 -> ... // Do stuff with x1
| _ -> failwith "Invalid type"
then x never matches option<obj>.
In order to make this work, I currently have to specify the type the option contains (e.g. if the function is passed an option<string>, and I downcast the parameter to that rather than option<obj>, the function works.
Is there a way I can downcast the parameter to option<obj> without specifying what type the option contains? I've tried option<_>, option<#obj>, and option<'a> with the same results.
By way of background, the parameter needs to be of type obj because I'm writing an interface for a monad, so Bind needs to bind values of different types depending on the monad that implements the interface. This particular monad is a continuation monad, so it just wants to make sure the parameter is Some(x) and not None, then pass x on to rest. (The reason I need the interface is because I'm writing a monad transformer and I need a way to tell it that its parameter monads implement bind and return.)
Update: I managed to get around this by upcasting the contents of the option before it becomes a parameter to this function, but I'm still curious to know if I can type-test or cast an object (or generic parameter) to an option without worrying about what type the option contains (assuming of course the cast is valid, i.e. the object really is an option).
There isn't any nice way to solve this problem currently.
The issue is that you'd need to introduce a new generic type parameter in the pattern matching (when matching against option<'a>), but F# only allows you to define generic type parameters in function declarations. So, your only solution is to use some Reflection tricks. For example, you can define an active pattern that hides this:
let (|SomeObj|_|) =
let ty = typedefof<option<_>>
fun (a:obj) ->
let aty = a.GetType()
let v = aty.GetProperty("Value")
if aty.IsGenericType && aty.GetGenericTypeDefinition() = ty then
if a = null then None
else Some(v.GetValue(a, [| |]))
else None
This will give you None or Some containing obj for any option type:
let bind (x : obj) rest =
match x with
| SomeObj(x1) -> rest x1
| _ -> failwith "Invalid type"
bind(Some 1) (fun n -> 10 * (n :?> int))
I am not certain why you need to get your input as obj, but if your input is an Option<_>, then it is easy:
member t.Bind (x : 'a option, rest : obj option -> 'b) =
let x = // val x : obj option
x
|> Option.bind (box >> Some)
rest x
To answer your last question: you can use a slight variation of Tomas' code if you need a general-purpose way to check for options without boxing values beforehand:
let (|Option|_|) value =
if obj.ReferenceEquals(value, null) then None
else
let typ = value.GetType()
if typ.IsGenericType && typ.GetGenericTypeDefinition() = typedefof<option<_>> then
let opt : option<_> = (box >> unbox) value
Some opt.Value
else None
//val ( |Option|_| ) : 'a -> 'b option
let getValue = function
| Option x -> x
| _ -> failwith "Not an option"
let a1 : int = getValue (Some 42)
let a2 : string = getValue (Some "foo")
let a3 : string = getValue (Some 42) //InvalidCastException
let a4 : int = getValue 42 //Failure("Not an option")