Given the following contrived active pattern:
let (|TypeDef|_|) (typeDef:Type) (value:obj) =
if obj.ReferenceEquals(value, null) then None
else
let typ = value.GetType()
if typ.IsGenericType && typ.GetGenericTypeDefinition() = typeDef then Some(typ.GetGenericArguments())
else None
The following:
let dict = System.Collections.Generic.Dictionary<string,obj>()
match dict with
| TypeDef typedefof<Dictionary<_,_>> typeArgs -> printfn "%A" typeArgs
| _ -> ()
gives the error:
Unexpected type application in pattern matching. Expected '->' or other token.
But this works:
let typ = typedefof<Dictionary<_,_>>
match dict with
| TypeDef typ typeArgs -> printfn "%A" typeArgs
| _ -> ()
Why is typedefof (or typeof) not allowed here?
Even if you're using a parameterized active pattern (where the argument is some expression), the compiler parses the argument as a pattern (as opposed to an expression), so the syntax is more restricted.
I think this is essentially the same problem as the one discussed here: How can I pass complex expression to parametrized active pattern? (I'm not sure about the actual compiler implementation, but the F# specification says that it should parse as a pattern).
As a workaround, you can write any expression inside a quotation, so you could do this:
let undef<'T> : 'T = Unchecked.defaultof<_>
let (|TypeDef|) (typeExpr:Expr) (value:obj) =
let typeDef = typeExpr.Type.GetGenericTypeDefinition()
// ...
let dict = System.Collections.Generic.Dictionary<string,obj>()
match dict with
| TypeDef <# undef<Dictionary<_,_>> #> typeArgs -> printfn "%A" typeArgs
| _ -> ()
Adding to Tomas' answer, the troublesome syntax in this case appears to be with the explicit type arguments. Another workaround is to use a dummy parameter to transmit the type information
let (|TypeDef|_|) (_:'a) (value:obj) =
let typeDef = typedefof<'a>
if obj.ReferenceEquals(value, null) then None
else
let typ = value.GetType()
if typ.IsGenericType && typ.GetGenericTypeDefinition() = typeDef then Some(typ.GetGenericArguments())
else None
let z =
let dict = System.Collections.Generic.Dictionary<string,obj>()
match dict with
| TypeDef (null:Dictionary<_,_>) typeArgs -> printfn "%A" typeArgs
| _ -> ()
Related
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).
I am working on a function that pattern matches some of my user-defined types in f# and converts them to strings. Part of the code looks like the following:
let gsToString (gs : general_structure) : string =
match gs with
| Date(Scattered(Eom(Ascending))) -> "Date(Scattered(Eom(Ascending)))"
| Date(Scattered(Eom(SameDate(dt)))) -> "Date(Scattered(Eom(SameDate(" + dt.ToString() + "))))"
| Number(AllNegative(Int1(Neither))) -> "Number(AllNegative(Int1(Neither)))"
| Number(AllNegative(Int1(SameInt(si)))) -> "Number(AllNegative(Int1(SameFloat(" + si.ToString() + "))))"
There are many other types being matched in this function, but these should be enough to convey the issue. Additionally, the types causing problems are:
| SameDate of System.DateTime
| SameFloat of float
Obviously, It is pretty trivial to do the first pattern matching function that converts my general_structure types to strings. However, a problem arises in my next function (which needs to be called later on in the code), where I need to reconvert the string representation back to a general_structure. The problem areas look like the following:
let stringToGS (str : string) : general_structure =
match str with
| "Date(Scattered(Eom(Ascending)))" -> Date(Scattered(Eom(Ascending)))
| "Date(Scattered(Eom(SameDate(dt))))"-> Date(Scattered(Eom(SameDate(System.DateTime.Parse dt))))
| "Number(AllNegative(Int1(Neither)))" -> Number(AllNegative(Int1(Neither)))
| "Number(AllPositive(Float1(SameFloat(sf))))" -> Number(AllPositive(Float1(SameFloat((float) sf))))
Although the first and the third cases in the stringToGS function work just fine, I am unable to find a way to convert the others back to their original form. If there any way to take a string inside of a pattern matching statement (in this case it would be dt and fs) and somehow parse only that portion of the pattern in order to return a different value (in this case I am trying to make them System.DateTimes and Floats, respectively) and return then to their original forms of:
Date(Scattered(Eom(SameDate(dt))))
Number(AllPositive(Float1(SameFloat(sf))))
? I would appreciate any help.
EDIT:
I was able to resolve the problem by doing something like the following with if statements for the cases that were causing problems:
if str.Contains("Scattered(Eom(SameDate")
then
let p1 = str.IndexOf(")")
let p2 = str.LastIndexOf("(")
let dt1 = str.Remove(p1)
let dt2 = dt1.Substring(p2 + 1)
let date = System.DateTime.Parse dt2
Date(Scattered(Eom(SameDate(date))))
Then, I could just do the normal pattern matching on all of the types that did not contain nested data.
You could also use active patterns, if there is a limited amount of classes and you don't want to use a serialization library:
open System
let (|RegexMatch|_|) pattern input =
let matches = System.Text.RegularExpressions.Regex.Matches(input, pattern)
if matches.Count = 1 then Some matches.[0].Groups.[1].Value
else None
type GeneralStructure =
| NoPayload
| DatePayload of DateTime
| StringPayload of string option
let toString = function
| NoPayload -> "NoPayload"
| DatePayload dt -> sprintf "DatePayload(%d)" <| dt.ToBinary()
| StringPayload None -> "StringPayload(None)"
| StringPayload (Some s) -> sprintf "StringPayload(Some(%s))" s
let fromString = function
| "NoPayload" -> NoPayload
| "StringPayload(None)" -> StringPayload None
| RegexMatch #"DatePayload\((.*)\)" dt -> DatePayload <| DateTime.FromBinary(Int64.Parse dt)
| RegexMatch #"StringPayload\(Some\((.*)\)\)" msg -> StringPayload <| Some msg
| o -> failwithf "Unknown %s %s" typeof<GeneralStructure>.Name o
let serialized = StringPayload <| Some "Foo" |> toString
let deserialized = fromString serialized
let serialized' = DatePayload DateTime.UtcNow |> toString
let deserialized' = fromString serialized'
// val serialized : string = "StringPayload(Some(Foo))"
// val deserialized : GeneralStructure = StringPayload (Some "Foo")
// val serialized' : string = "DatePayload(5247430828937321388)"
// val deserialized' : GeneralStructure = DatePayload 06.08.2015 18:04:10
Note that the regex is not foolproof, I made that up just to fit these cases.
The following snippet illustrates the error I'm getting. Even though both match branches return the same thing; I get error, "This expression was expected to have type unit but here has type 'a -> unit" I have no clue what the compiler wants here...
open System.IO
let FileContent contents =
match contents with
| "" -> None
| c -> Some(c)
let WriteSomething (contents:string) =
let writer = new StreamWriter("")
writer.Write( contents ) |> ignore
let DoStuffWithFileContents =
let reader = new StreamReader( "" )
let stuff = reader.ReadToEnd()
match stuff |> FileContent with
| Some(c) -> WriteSomething c
|> ignore
| None -> ignore // <- error on "ignore"
The ignore operator is actually a function that takes a single input and returns the unit type (F#'s equivalent of void). So when you have -> ignore you're returning the ignore function.
Instead, use () to represent the value of the unit type:
| Some(c) -> WriteSomething c
|> ignore
| None -> ()
But actually, since StreamWriter.Write returns void, all these ignore's are unnecessary. You could just as easily write this as:
let WriteSomething (contents:string) =
let writer = new StreamWriter("")
writer.Write(contents)
let DoStuffWithFileContents =
let reader = new StreamReader("")
let stuff = reader.ReadToEnd()
match stuff |> FileContent with
| Some(c) -> WriteSomething c
| None -> ()
Or even better, use Option.iter:
let WriteSomething (contents:string) =
let writer = new StreamWriter("")
writer.Write(contents)
let DoStuffWithFileContents =
let reader = new StreamReader("")
let stuff = reader.ReadToEnd()
stuff |> FileContent |> Option.iter(WriteSomething)
By returning ignore on your last line you are returning a function, not a simple value.
The point of ignore is to convert things to (). Your last line can simply return () directly.
I'm trying to learn F# and have put together the following code:
open System.Collections.Generic
type Version = Version of int
type AggregateId = AggregateId of int
type IEventdata = interface end
type EventMeta = EventMeta of AggregateId * Version
type Event = Event of EventMeta * IEventdata
type ICommandData = interface end
type CommandMeta = CommandMeta of AggregateId * Version
type Command = CommandMeta * ICommandData
type EventStore = {
GetEvents : AggregateId -> Event seq;
Insert : Event seq -> unit
}
let commandDispatcherFactory (mapping: (Command -> Event seq -> Event seq), es: EventStore) =
(
fun (command: Command) ->
match command with
| (CommandMeta(aggregateId, version), commandData) ->
let events = es.GetEvents (aggregateId)
let resultingEvents = mapping command events
(es.Insert(resultingEvents))
)
type DummyCommand =
| Command1
interface ICommandData
type DummyCommand2 =
| Command2
interface ICommandData
type UnsupportedCommand =
| Command3
let dummyMapping = (fun ((command, commandData):Command) (events: Event seq) ->
match commandData with
| :? DummyCommand as dummyCommand ->
(printfn "Handling Command: %A" dummyCommand)
Seq.empty<Event>
| :? DummyCommand2 as dummyCommand ->
(printfn "Handling Command: %A" dummyCommand)
Seq.empty<Event>
| _ ->
(printfn "Unsupported command: %A" commandData)
Seq.empty<Event>
)
let dummyEs = {
GetEvents = (fun (aggregateId) -> Seq.empty<Event>);
Insert = (fun (events:Event seq) -> ())
}
dummyMapping (CommandMeta(AggregateId(1),Version(1)), Command1) Seq.empty<Event>
dummyMapping (CommandMeta(AggregateId(1),Version(1)), Command2) Seq.empty<Event> // Why does this work?
let commandDispatcher = commandDispatcherFactory(dummyMapping, dummyEs)
commandDispatcher (CommandMeta(AggregateId(1),Version(1)), Command1)
commandDispatcher (CommandMeta(AggregateId(1),Version(1)), Command2) // this doesn't work
I know it might be a wall of text, but I didn't manage to narrow it down. It's the last call to my commandDispatcher that doesn't work, and I can't figure out why. I know there probably are other things to do better, but that is something I'm working on :)
Here's a cut down repro:
let f x (y:System.IComparable) = ()
let g = f 1
g 3
g "test" // not okay
let g' : System.IComparable -> unit = f 1
g' 3
g' "test" // okay
let g'' x = f 1 x
g'' 3
g'' "test" // okay
What happens is that the compiler infers a generic type for g (g:('a -> unit) when 'a :> System.IComparable in the case of my example). However, g is a syntactic value and therefor can't be generic (see questions about the value restriction), so the compiler needs to pick a specific type. If you don't use g at all, you'll get a compiler error. If you use it at exactly one concrete type, the compiler will pick that type. If you use it at more than one type, the compiler will specialize based on the first type used and throw an error after that.
Two workarounds are to either annotate with a non-generic type or to eta-expand so that g isn't a syntactic value.
The compiler is specializing the type of commandDispatcher based on the initial usage. You can fix it with a cast:
commandDispatcher (CommandMeta(AggregateId(1),Version(1)), Command1 :> ICommandData)
commandDispatcher (CommandMeta(AggregateId(1),Version(1)), Command2) //works
I continue to work on a printer for F# quoted expressions, it doesn't have to be perfect, but I'd like to see what is possible. The active patterns in Microsoft.FSharp.Quotations.Patterns and Microsoft.FSharp.Quotations.DerivedPatterns used for decomposing quoted expressions will typically provide MemberInfo instances when appropriate, these can be used to obtain the name of a property, function, etc. and their "declaring" type, such as a module or static class. The problem is, I only know how to obtain the CompiledName from these instances but I'd like the F# name. For example,
> <# List.mapi (fun i j -> i+j) [1;2;3] #> |> (function Call(_,mi,_) -> mi.DeclaringType.Name, mi.Name);;
val it : string * string = ("ListModule", "MapIndexed")
How can this match be rewritten to return ("List", "mapi")? Is it possible?
FYI, here is my final polished solution from Stringer Bell and pblasucci's help:
let moduleSourceName (declaringType:Type) =
FSharpEntity.FromType(declaringType).DisplayName
let methodSourceName (mi:MemberInfo) =
mi.GetCustomAttributes(true)
|> Array.tryPick
(function
| :? CompilationSourceNameAttribute as csna -> Some(csna)
| _ -> None)
|> (function | Some(csna) -> csna.SourceName | None -> mi.Name)
//usage:
let sourceNames =
<# List.mapi (fun i j -> i+j) [1;2;3] #>
|> (function Call(_,mi,_) -> mi.DeclaringType |> moduleSourceName, mi |> methodSourceName);
You can use F# powerpack for that purpose:
open Microsoft.FSharp.Metadata
...
| Call(_, mi, _) ->
let ty = Microsoft.FSharp.Metadata.FSharpEntity.FromType(mi.DeclaringType)
let name = ty.DisplayName // name is List
However, I don't think if it's possible to retrieve function name with powerpack.
Edit:
As hinted by pblasucci, you can use CompilationSourceName attribute for retrieving source name:
let infos = mi.DeclaringType.GetMember(mi.Name)
let att = infos.[0].GetCustomAttributes(true)
let fName =
(att.[1] :?> CompilationSourceNameAttribute).SourceName // fName is mapi