F#: Quotation with type definition? - f#

I'm playing around with quotations and I can't see an expression pattern for type definitions. Is there really not one, or am I missing something?
<## type MyType (name:string) =
member x.Name = name ##>
Gives "Unexpected keyword 'type' in quotation literal."

You can't. You can only quote code, that is to say, any valid F# expression. Type definitions are not considered as code, but definitions.
What you might want to do is put ReflectedDefinition attribute on a type members:
type MyType (name : string) =
[<ReflectedDefinition>] member x.Name = name
If you want to retrieve the AST of members that have ReflectedDefinition you can use Expr.TryGetReflectedDefinition function.
E.g, this sample code prints ASTs of all reflected definition members of MyType:
open Microsoft.FSharp.Quotations
open System.Reflection
type MyType (name : string) =
[<ReflectedDefinition>] member x.Name = name
let mis = typeof<MyType>.GetMembers()
for mi in mis do
try
match Expr.TryGetReflectedDefinition(mi :?> MethodBase) with
| Some(e) -> printfn "%A" e
| None -> ()
with _ -> ()
()

Related

Discriminated Union label to string

given the following Discriminated Union:
type A = B of string | C of int
How can I get the constructor B name?
A.B.ToString()
// would return something like:
val it : string = "FSI_0045+it#109-19"
// when I desire
val it : string = "B"
for example with this type it works:
type D = E | F
D.E.ToString();;
val it : string = "E"
I normally get the string name of an instance of the DU with
let stringFromDU (x: 'a) =
match FSharpValue.GetUnionFields(x, typeof<'a>) with
| case, _ -> case.Name
But in this case, I do not have an instance, I just want to serialize the label name.
If you enable the latest language version, e.g. by passing --langversion:preview to FSI or setting
<PropertyGroup>
<LangVersion>preview</LangVersion>
</PropertyGroup>
in your .fsproj, the following will work:
type A = B of int
let n = nameof A.B
Note: with F# 5 this will be supported out of the box :-)
You're using FSharpValue from FSharp.Reflection namespace in your example. Note that there's another class in that library for handling scenarios where you want to work with types only, FSharpType.
let cases = FSharpType.GetUnionCases(typeof<A>)
Outside of unions, it also provides helpers for other operations on F# native types.

The type HtmlNode doesn't match the type 'Nullable<'a>'?

I'm using HtmlAgilityPack to parse some Html files. The following code
let html = "<div>...</div>"
let doc = new HtmlDocument()
doc.LoadHtml(html)
let node = doc.DocumentNode
let x = node.SelectSingleNode("//div/#xyz")
|> Option.ofNullable
The last line got the error of
The type HtmlNode doesn't match the type 'Nullable<'a>'
The following code can convert it
let ofNull = function
| null -> None
| x -> Some x
Is there any built-in construct to do it? I don't want my definition of ofnull scatter everywhere in the code/script.
The built-in function you are looking for is Option.ofObj, not Option.ofNullable.
The Option.ofNullable converts a value of type System.Nullable<'a>, which is a .NET type that is used for turning value types into nullable types. C# has langauge support for this and you can use, for example, int? to define type that is similar to option<int> in F#.
The Option.ofObj function takes any .NET obj (actually, a generic type that has a null as a value) and returns Some if it is not null or None if it is null. The types are:
Option.ofNullable : System.Nullable<'a> -> 'a option
Option.ofObj : 'a -> 'a option

How to call a function from the quotation of a invoke in type provider?

I have a type provider which gives me the error "Type mismatch when splicing expression into quotation literal".
I extracted the code below in order to reproduce the issue in a smaller context.
let f (s : string) : string = s //some dummy implementation
let t = ProvidedTypeDefinition(asm, ns, "Root", Some typeof<obj>)
let ctor = ProvidedConstructor(parameters = [],
InvokeCode = (fun args -> <## "root" :> obj ##>)) :> MemberInfo
let prop = ProvidedProperty(propertyName = "SomeProperty",
parameters = [],
propertyType = typeof<string>,
GetterCode = (fun args -> <## f %%(args.[0]) ##>)) :> MemberInfo
do t.AddMembers [ctor; prop]
t.SetBaseType typeof<obj>
...and when I use the type provider like
let root = Provided.Root()
let a = root.SomeProperty
I get the error:
Error: The type provider 'typeproviders.providerpoc+MyProvider' reported an error in the context of provided type
'typeproviders.providerpoc.Provided.Root', member 'get_Menu'.
The error: Type mismatch when splicing expression into quotation
literal.
The type of the expression tree being inserted doesn't match
the type expected by the splicing operation.
Expected 'System.Object', but received type 'System.String'.
Consider type-annotating with the expected expression type, e.g., (%% x :
string) or (%x : string).. Parameter name: receivedType.
How can I write the quotation to be able to call a function inside the quotation?
Thanks!
What the error message is saying is that you are putting a quoted expression of type obj in a place where a quoted expression of type string is expected.
I suspect this is happening when creating the GetterCode in the provided property:
GetterCode = (fun args -> <## f %%(args.[0]) ##>)
Here, args is an array of quoted expressions where each expression is of type obj, but the function f expects a string and so the quotation filling the hole using %% should be of type string
Adding a type conversion that would turn the obj into string should do the trick:
GetterCode = (fun args -> <## f (unbox<string> (%%(args.[0]))) ##>)

Why can't F# compiler infer type in this case?

It seems that the p argument in printPerson function can't be inferred to be Person, but intelisense shows for both of printPerson calls that I'm passing p : Person. Help me understand what I'm doing wrong please?
type Person (name:string) =
member e.Name = name
type EmployeeNode =
| Leader of Person * int * list<EmployeeNode>
| Employee of Person * int
let printPerson level p =
printfn "%s %s" <| String.replicate (level) "#" <| p.Name
let rec print node =
match node with
| Employee(p, level) -> printPerson level p
| Leader(p, level, nodes) ->
printPerson level p
List.iter print nodes
Multiple types could have a member this.Name, even if they don't in this example, so the compiler doesn't know that you meant Person. Say for example you had
type Person (name : string) =
member this.Name = name
type School (name : string, address : string) =
member this.Name = name
member this.Address = address
let printName x = printfn "%s" x.Name // This is a type error.
The compiler can't tell which you meant - Person or School. It doesn't matter if you haven't defined another type with a member of the same name, the compiler still won't take the type because things like type extensions can add members onto types after compilation.
Intellisense knows the types you're trying to pass when you call the function, but that's not the same as the compiler enforcing type-safety. In general, any function which accesses class methods or members will need a type annotation for that class.
To fix your example, you just need to change to
let printPerson level p =
to
let printPerson level (p : Person) =
If you are working with simple immutable data containers in F#, you will find it easier to write idiomatic code, making use of type inference, with Records rather than standard .NET classes.
You would change your definition of Person as follows:
type Person = {Name : string}
If you use a record, you do not need to use a type annotation and can keep your code as it is:
let printPerson level p =
printfn "%s %s" <| String.replicate (level) "#" <| p.Name
I would recommend this approach, especially because records give you additional bonuses for free such as automatic structural equality and comparison.
If you use a standard .NET class you must provide a type annotation to disambiguate the specific type you are referring to which exposes the Name property:
type Person (name:string) =
member e.Name = name
let printPerson level (p : Person) =
printfn "%s %s" <| String.replicate (level) "#" <| p.Name
In printPerson, the only information about p the compiler has is that it has a Name member. Since there can be other types than Person which have one, it can't infer p to be Person.
In calls of printPerson the type of p is determined from the pattern, not from the call.

Why is Type Annotation Required Here?

This function has the signature: (UnionCaseInfo -> bool) -> 'T option
let private findCase<'T> f =
match FSharpType.GetUnionCases typeof<'T> |> Array.filter f with
|[|case|] -> Some (FSharpValue.MakeUnion(case,[||]) :?> 'T)
|_ -> None
This function, which calls the above function, has the signature: int -> obj
let CreateFromId<'T> id =
match findCase (fun case -> case.Tag = id) with
| Some c -> c
| None -> failwith (sprintf "Lookup for union case by \"%d\" failed." id)
In the pattern for CreateFromId, intellisense shows that c is inferred to be of type obj, even though it shows the correct signature for findCase. Why does the type seem to have been "lost" in the pattern?
(I can workaround this by specifying the return type of CreateFromId to 'T)
Because the type parameter 'T is not referenced in the body of the function, so type inference has no way to know your intention was to name 'T the return type.
So you can either add it in the body as the return type (as you already figured it out) or remove it from the declaration:
let CreateFromId id = ...
By removing it works because F# does automatic generalization, the only different is it will use an arbitrary name for the type variable, but even if you want to name that type variable 'T what I would do is add it as a return type but no in the declaration between brackets:
let CreateFromId id : 'T = ...
The type of CreateFromId was inferred to by int -> obj because there's nothing "linking" the two 'T type arguments on your functions.
findCase is properly generic over 'T, but CreateFromId is only declared to be generic, and the generic type argument is never used.
Annotating the function with the desired type is good enough to make the types line up. You can also call findCase explicitly providing the type:
let CreateFromId<'T> id =
match findCase<'T> (fun case -> case.Tag = id) with
| Some c -> c
| None -> failwith (sprintf "Lookup for union case by \"%d\" failed." id)
Or as the other answer suggests, just drop the 'T from CreateFromId and let type inference do its thing.

Resources