When try the following code in F# interactive
> let a = None
- let b = (a, Some 1);;
> b;;
val it : 'a option * int option = (null, Some 1)
It show that b has type 'a option * int option, the type of b is correct. However, the value of first element of the tuple is null, not None, Why?
When try to verify the first element of the tuple whether really is null
printfn "%s" (match b with (null, _) -> "null" | _ -> "not null");;
it gives the following error
error FS0043: The type ''a option' does not have 'null' as a prope r
value
When try to get the fisrt value in tuple,
let c = fst b;;
it gives
error FS0030: Value restriction. The value 'c' has been inferred to
have generic type
val c : '_a option Either define 'c' as a simple data term, make it a function with explicit argume nts or, if you do not intend for it
to be generic, add a type annotation.
The internal representation for the value None is indeed a null value. But that is the internal representation, the compiler identifies null and None as two completely different values, so you cannot compare them as they are different types. That is why you get: error FS0043.
This is actually something to be careful of:
let a = None
let b = (a, Some 1)
let print v = printfn "%A" v
sprintf "%A" a |> print // "<null>"
sprintf "%A" b |> print // "(None, Some 1)"
sprintf "%O" a |> print // "<null>"
sprintf "%O" b |> print // "(, Some(1))"
string a |> print // ""
string b |> print // "(, Some(1))"
a .IsNone |> print // true
a .IsSome |> print // false
a .GetType() |> print // System.NullReferenceException: Object reference not set to an instance of an object.
a .ToString() |> print // System.NullReferenceException: Object reference not set to an instance of an object.
(fst b).ToString() |> print // System.NullReferenceException: Object reference not set to an instance of an object.
(snd b).ToString() |> print // "Some(1)"
... because calling some methods on a None value throws the dreaded NullReference Exception and converting to string is also erratic.
Regarding error FS0030 basically values cannot be generic. This has been discussed many times in SO.
Values from Discriminated Unions seem to be treated in a special way, they seem to be granted an exception, for instance these are generic and still Ok:
type MyDU<'A, 'B> =
| ValNo
| ValA of 'A
| ValB of 'B
let v1 = ValNo // MyDU<'a,'b> double generic but Ok
let v2 = ValA 1 // MyDU<int,'a> generic but Ok
let v3 = ValB 1 // MyDU<'a,int> generic but Ok
but these are not Ok
let valNo() = ValNo
let valA a = ValA a
let valB b = ValB b
let w1 = valNo() // MyDU<'_a,'_b> double generic not Ok
let w2 = valA 1 // MyDU<int,'_a> generic not Ok
let w3 = valB 1 // MyDU<'_a,int> generic not Ok
Related
The following code returns Some "Test" stead of None. Basically, I'm trying to implement the C# code of cObj?.B.A.P.
// Setup
[<AllowNullLiteral>]
type A() =
member x.P = "Test"
[<AllowNullLiteral>]
type B(a:A) =
member x.A = a
[<AllowNullLiteral>]
type C(b:B) =
member x.B = b
// Test
let aObj: A = null
let cObj = new C(new B(aObj))
let r =
cObj |> Option.ofObj
|> Option.map(fun c -> c.B)
|> Option.map(fun b -> b.A)
|> Option.map(fun a -> a.P) // Expect return None since a is null
// printfn "%A" a; will print <null>.
// How can F# got property of null object?
r
It seems F# doesn't treat null as None in the Option.map. Is there a simple fix to make it return None as soon as a null is found?
F#, unlike C#, tries to be explicit everywhere. In the long run this leads to more maintainable and correct programs.
In particular, null has absolutely nothing to do with Option. null is not the same as None. None is a value of type Option, while null is this very vague concept - a value that can be of any type.
If you would like to return None when the argument is null and Some otherwise, what you need is Option.bind, not Option.map. Option.bind takes a function that takes a value (extracted from a previous Option) and returns another Option. Something like this:
let maybeC = Option.ofObj cObj
let maybeB = maybeC |> Option.bind (c -> Option.ofObj c.B)
let maybeA = maybeB |> Option.bind (b -> Option.ofObj b.A)
let maybeP = maybeA |> Option.bind (a -> Option.ofObj a.P)
Or in one go:
let maybeP =
Option.ofObj cObj
|> Option.bind (c -> Option.ofObj c.B)
|> Option.bind (b -> Option.ofObj b.A)
|> Option.bind (a -> Option.ofObj a.P)
If you do this sort of thing very frequently, you can combine Option.bind and Option.ofObj calls and encode that as a separate function:
let maybeNull f = Option.bind (x -> Option.ofObj (f x))
let maybeP =
Option.ofObj cObj
|> maybeNull (c -> c.B)
|> maybeNull (b -> b.A)
|> maybeNull (a -> a.P)
However, if you find yourself buried in nulls like that, I would suggest that perhaps your domain design is not thought through very well. Nulls are not a good modeling tool, they should be avoided as much as possible. I encourage you to rethink your design.
The first argument to Option.map is a function 'T -> 'U. Its parameter is of type 'T, not 'T option. So, in your last lambda fun a -> a.P, a null argument denotes a null of type A, not a null of type A option.
Because the member P of type A just returns the string "Test," the call succeeds and returns even though the receiver is null. If you try to use the self identifier in the body of P, you will instead get a null reference exception.
I am trying figure out if a generic type wrapped in a rop result is a list or not. This is what I tried but I got errors.
let checkType (result : RopResult<'tSuccess, 'errors>) =
match result with
| Success (s, msg) ->
match s with
| :? [] -> // error here
Sample
let isList<'s> () = true
let processList (ls : 'domain list) = true
let processType (s : 'domain) = true
let checkType (result : RopResult<'tSuccess, 'errors>) =
match result with
| Success (s, msg) ->
match s with
| s when isList<s>() -> processList s
| _ -> processType s
| Failure (x) -> false
I'll first explain the technicalities of how to get your code to work, and then try to convince you (as the other folks on this thread) that it may not be the right way to approach your problem.
Firstly, your match statement has a syntax error. You would write the type test and the cast in one swoop as
match s with
| :? List<int> as theIntList -> ...do something with theIntList ...
When you add that to your code, the F# compiler will complain "The runtime coercion or type test ... involves an indeterminate type. ... Further type annotations are needed". Fix that by being more specific about what kind of result your checkType is processing: it is some System.Object instance and the message, so you'd write:
let checkType (result : Result<obj*string, 'errors>) =
match result with
| Success (s, msg) ->
match s with
| :? List<int> as theIntList -> ... do something
Note that you can't change that to a generic thing like List<_> - F# will do the type test and the cast in one go, and would not hence know what to cast to. If you try to, you will see warnings that your List<_> has been inferred to be List<obj>
Having said all that: Using obj is not the idiomatic way to go, as others have tried to point out already. The answers of #robkuz and #TheInnerLight contain all you need: A map function, functions that operate on individual result types, which then becomes nicely composable:
let map f x =
match x with
| Success (s, msg) -> Success (f s, msg)
| Failure f -> Failure f
// This will automatically be inferred to be of type Result<(int list * string), 'a>
let myFirstResult = Success ([1;2], "I've created an int list")
// This will automatically be inferred to be of type Result<(string list * string), 'a>
let mySecondResult = Success (["foo"; "bar"], "Here's a string list")
// Process functions for specific result types. No type tests needed!
let processIntList (l: int list) = Seq.sum l
let processStringList = String.concat "; "
// This will automatically be inferred to be of type Result<(int * string), 'a>
let mapFirst = myFirstResult |> map processIntList
// This will automatically be inferred to be of type Result<(string * string), 'a>
let mapSecond = mySecondResult |> map processStringList
I am not sure if I really understand your problem.
In general if you have some polymorphic type (like your RopResult) and you want to process the polymorphic part of it a good approach in F# would be
to disentagle your code into a wrapper code and a processor code where your processor code is delivered via a higher order function for the processing part.
Example:
type RopResult<'tSuccess, 'tError> =
| Success of 'tSuccess
| Error of 'tError
let checkType (process: 'tSuccess -> 'tResult) (result : RopResult<'tSuccess, 'tError>) =
match result with
| Success s -> process s |> Success
| Error e -> Error e
and
let processList (ls : 'domain list) = true
let processType (s : 'domain) = true
and then you
checkType processList aListWrappedInResult
checkType processType aTypeWrappedInResult
Assuming you wanted to determine whether a supplied value was of a generic list type, you could do this:
let isList value =
let valueType = value.GetType()
match valueType.IsGenericType with
|true -> valueType.GetGenericTypeDefinition() = typedefof<_ list>
|false -> false
Example usage:
isList [5];;
val it : bool = true
isList ["a", "b"];;
val it : bool = true
isList "a";;
val it : bool = false
When working with something like RopResult, or more formally, Either, it's helpful to define the map function. The map function takes a function 'a -> 'b and gives you a function which operates in some elevated domain, e.g. RopResult<'a,'c> -> RopResult<'b,'c>.
This is analogous to List.map : ('a ->'b) -> 'a List -> 'b List.
We define it like this:
let map f v =
match v with
|Success sv -> Success (f sv)
|Failure fv -> Failure (fv)
You can then use isList on RopResults by simply doing:
ropResult |> map isList
Others here are warning you in the comments that there may be potential issues surrounding how you actually process the results once you've determined whether the type is a list or not. Specifically, you will need to ensure that the return types of your processList and processType functions are the same (although I would recommend revisiting the naming of processType and call it processValue instead. Since you are not operating on the type, I think the name is confusing).
How do I invoke a function value that serves as a parameter on a function?
Specifically, my goal is to leverage a parameter of a function in which the parameter is actually a function.
In my case, I am trying to implement an interface for logging data.
Here's my code:
let logToFile (filePath:string) (message:string) =
let file = new System.IO.StreamWriter(filePath)
file.WriteLine(message)
file.Close()
let makeInitialDeposit deposit =
let balance = deposit |> insert []
sprintf "Deposited: %f" balance
let logDeposit deposit (log:'medium ->'data -> unit) =
deposit |> makeInitialDeposit
|> log
Note the following function:
let logDeposit deposit (log:'medium ->'data -> unit) =
deposit |> makeInitialDeposit
|> log
I get a compile error on the log function:
This construct causes code to be less generic than indicated by the
type annotations. The type variable 'medium has been constrained to be
type 'string'.
I understand that makeInitialDeposit returns string.
However, that string type is mapped to the generic type 'data.
Hence, a generic can be of any type right?
I then tried supplying the medium (i.e. file) argument:
let logDeposit deposit (log:'medium ->'data -> unit) medium =
deposit |> makeInitialDeposit
|> log medium
Then my error got updated to:
This construct causes code to be less generic than indicated by the
type annotations. The type variable 'data has been constrained to be
type 'string'.
My Goal
Ultimately, I just want to have an interface called log and pass in an implementation of that interface (i.e. logToFile).
Any guidance on how I should interpret the compile error based on my initial interpretation?
Insert function dependencies
let getBalance coins =
coins |> List.fold (fun acc d -> match d with
| Nickel -> acc + 0.05
| Dime -> acc + 0.10
| Quarter -> acc + 0.25
| OneDollarBill -> acc + 1.00
| FiveDollarBill -> acc + 5.00) 0.00
let insert balance coin =
coin::balance |> getBalance
The issue is that you have defined log :'medium ->'data -> unit.
You then take the result of deposit |> makeInitialDeposit, which has type string and pipe it into the log function. The compiler, logically, then infers that 'medium = string.
If you accept a 'medium argument in your logDeposit function then you simply move that inference along a step, deposit |> makeInitialDeposit is still a string so now 'data = string.
I think you are struggling though because these functions don't well model your domain and your logging logic is bleeding out into the rest of your code.
Why does makeInitialDeposit return a string?
Why does getBalance return a float but insert accepts a Coin list as its balance argument?
I would start by making a logging function that accepts three arguments:
let logToFile (filePath:string) (formatf : 'data -> string) data =
use file = new System.IO.StreamWriter(filePath)
file.WriteLine(formatf data)
data
It has type filePath : string -> (formatf : 'data -> string) -> (data : 'data) -> data. It accepts a path to log to, a function that formats something of type 'data as a string and some 'data to log to the file. Finally, it returns the data argument you supplied unchanged. That means you can, in principle, insert logging of any arbitrary value in your code anywhere.
I then set up some functions in the domain like this:
let valueOf = function
| Nickel -> 0.05m
| Dime -> 0.10m
| Quarter -> 0.25m
| OneDollarBill -> 1.00m
| FiveDollarBill -> 5.00m
let totalValue coins =
coins |> List.fold (fun acc coin -> acc + valueOf coin) 0.0m
let insert coins coin = coin::coins // returns Coin list
let makeInitialDeposit deposit = insert [] deposit // returns Coin list
I can then use these functions, inserting logging at any arbitrary point:
let balance =
makeInitialDeposit OneDollarBill
|> logToFile "file.txt" (sprintf "Initial Deposit: %A")
|> totalValue
|> logToFile "file2.txt" (sprintf "Balance : $%M")
This approach lets you fit logging around your domain rather than building your domain around logging.
First off, it seems odd that you're passing in a string like "Deposited: 0.25" as your path name. What you probably wanted is to have message as your first parameter and filePath as your second parameter for logToFile. And accordingly for log to be 'data -> 'medium -> unit.
Getting that out of the way, the issue for your compiler error is that makeInitialDeposit returns a string and when you then pipe that result into log the constraint happens. One way to get it to work is to add another parameter to logDeposit to converts string to 'data like this:
let logDeposit deposit (log: 'data ->'medium-> unit) (stringConverter: string -> 'data) =
deposit |> makeInitialDeposit
|> stringConverter
|> log
With this something like let dummyLog (a:int) (b:string) = () will work assuming you pass in the appropriate converter from string to int.
There is some difficulty in getting Option type dynamically. Suppose I have a function:
let printType x =
if (box x) = null then printfn "the type is 'null'"
else printfn "the type is %A" (x.GetType())
And we have the output here:
printType 3 // the type is System.Int32
printType (Some(3)) // the type is Microsoft.FSharp.Core.FSharpOption`1[System.Int32]
printType None // the type is null
printType null // the type is null
How to differentiate between None and null when getting an expression's type?
At runtime, option None values are represented as null and so you cannot determine their runtime type. However, you could write a generic function that prints the static type of the option:
let printType (x:'T option) =
printfn "Static type is: %s" (typeof<'T>.Name)
Depending on what you need, this might or might not do what you need.
printType (None : int option) // Int32
printType (None : string option) // String
printType None // Object (the default used by the inference)
EDIT If you want to be able to call the function on any arguments, you can use typeof<'T> as in my example together with the rest of the logic as in the solution by Matthew. The following does the same thing as Matthew's snippet (but it does not create quotations for no good reason):
let printType (x:'T) =
let typeOfX = typeof<'T>
if not <| FSharpType.IsUnion(typeOfX) && (box x) = null then
printfn "Static type is %s, but the value is 'null'" typeOfX.Name
else
printfn "Static type is %s and value is not 'null'" typeOfX.Name
Edit: The code quotation is not needed and typeof can be used instead, see #Tomas Petricek's answer
You could use a code quotation to get the type from None
open Microsoft.FSharp.Reflection
let printType x =
let typeOfX = <# x #>.Type
if not <| FSharpType.IsUnion(typeOfX) && (box x) = null then
printfn "the type is 'null'"
else
printfn "the type is %A" typeOfX
Your input:
printType 3 // the type is System.Int32
printType (Some(3)) // the type is Microsoft.FSharp.Core.FSharpOption`1[System.Int32]
printType None // the type is Microsoft.FSharp.Core.FSharpOption`1[System.Object]
printType null // the type is 'null'
This can be a common problem when implementing interpreters. To add to the above solutions, you could also do the following:
open System
type TagObj (obj : obj, t : Type) =
member __.Obj = obj
member __.Type = t
and TagObj<'T>(value : 'T) =
inherit TagObj(value, typeof<'T>)
member __.Value = value
override __.ToString() = sprintf "%O" value
let mkTag x = TagObj<_>(x) :> TagObj
// examples
let t = mkTag (None : int option)
let t' = mkTag ()
t.Obj = t'.Obj // true
t.Type = t'.Type // false
This is roughly how constant values are represented in quotations as well.
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")