I need to specify, that my member property will return something like dynamic? in C#. Is possible use dynamic data type in F#?
type Data =
| Text of string
| Number of string
| Date of string
with
member x.Value
with get() : dynamic option =
match x with
| Text(value) ->
if value.Length > 0 then Some(value) else None
| Number(value) ->
let (success, number) = Decimal.TryParse value
if (success) then Some(number) else None
| Date(value) ->
let (success, date) = DateTime.TryParse value
if (success) then Some(date) else None
This code cannot be compiled, because return type is determined as string option from Text case. Keyword dynamic is unknown in F#. Any ideas?
Try to make this datatype:
type ThreeWay = S of string | N of Decimal | D of DateTime
or, use the System.Object type:
open System
type Data =
| Text of string
| Number of string
| Date of string
with
member x.Value
with get() : Object option =
match x with
| Text(value) ->
if value.Length > 0 then Some(value :> Object) else None
| Number(value) ->
let (success, number) = Decimal.TryParse value
if (success) then Some(number :> Object) else None
| Date(value) ->
let (success, date) = DateTime.TryParse value
if (success) then Some(date :> Object) else None
To get the value:
let d = Number("123")
let v = d.Value
match v with
| Some(x) -> x :?> Decimal // <-- TYPE CAST HERE
Related
Given the following:
type IFruit = interface end
type Avocado = { color : string; age : int } interface IFruit
let (|AvocadoTexture|) (a : Avocado) = if a.age < 7 then "firm" else "mushy"
... Why does this work:
let texture (f : IFruit) =
match f with
| :? Avocado as a -> if a.age < 7 then "firm" else "mushy"
| _ -> String.Empty
... but not this?
let texture (fruit : IFruit) =
match fruit with
| AvocadoTexture t -> t // "The type IFruit does not match the type Avocado"
| _ -> String.Empty
fruit may be any IFruit, but the AvocadoTexture Active Pattern only accepts the specific implementation Avocado, as per the type annotation of a.
If you want the Active Pattern to accept any IFruit, but only return a useful value for an Avocado, you can make it partial:
let (|AvocadoTexture|_|) (f : IFruit) =
match f with
| :? Avocado as a ->
if a.age < 7 then "firm" else "mushy"
|> Some
| _ -> None
Now your texture function works as you wanted:
let texture (fruit : IFruit) =
match fruit with
| AvocadoTexture t -> t
| _ -> String.Empty
Just bear in mind that there are Partial Active Patterns and Active Patterns. Active Patterns have up to 7 tags that something can be concretely matched against. Both forms are useful.
Active Patterns are better if you want the compiler to tell you all the places where you've missed handling a case after you've decided that you need an extra one. The compiler can be configured to flag this as an error rather than a warning if you want to be extra strict about it.
open System
type IFruit = interface end
type Avocado =
{ color : string; age : int }
interface IFruit
static member tryFromIFruit(x:IFruit) =
match x with
| :? Avocado -> Some(x:?>Avocado)
| _ -> None
let (|Firm|Mushy|) (a : Avocado) = if a.age < 7 then Firm else Mushy
let texture (fruit : IFruit) =
match fruit |> Avocado.tryFromIFruit with // we're not sure if it's an Avocado.
| Some(Firm) -> "firm" // use Some(SomethingElse()) when you want to collapse an extra layer of "match" statements.
| Some(Mushy) -> "mushy"
| None -> ""
texture ( { color = "green"; age = 4 } :> IFruit)
documentation: https://learn.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/active-patterns
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.
I have the following code. For the last two match, the first period has type of DateTime option and the second one has type of int. Why the second one doesn't have option?
let (|Integer|_|) (str: string) =
let mutable intvalue = 0
if Int32.TryParse(str, &intvalue) then Some(intvalue)
else None
let (|DateyyMM|) (str: string) =
let mutable date = new DateTime()
if DateTime.TryParseExact(str,
"yyyyMM",
Globalization.DateTimeFormatInfo.InvariantInfo,
Globalization.DateTimeStyles.None,
&date)
then Some(date)
else None
let (|ParseRegex|_|) regex str =
let m = Regex(regex).Match(str)
if m.Success
then Some (List.tail [ for x in m.Groups -> x.Value ])
else None
.....
match url with
| ParseRegex "....." [DateyyMM period] -> //period type is DateTime option
......
match downloadLink.Url with
| ParseRegex "....." [name; Integer period] -> // period type is int
......
The second case has no option because you added _| at the end of the declaration.
This is setup to allow for a shorthand in a match - so that rather than
match x with
|Some_long_function(Some(res)) -> ...
|Some_long_function(None) -> ...
you can just do
match x with
|Some_long_function(res) -> ...
|_ -> ...
See the MSDN page on active patterns for more: http://msdn.microsoft.com/en-us/library/dd233248.aspx (in particular the secion on partial patterns)
I have the following ViewModelBase in F# which I'm trying to build to learn F# with WPF.
module MVVM
open System
open System.Collections.ObjectModel
open System.ComponentModel
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns
open System.Reactive.Linq
module Property =
let ToName(query : Expr) =
match query with
| PropertyGet(a, b, list) ->
b.Name
| _ -> ""
let SetValue<'t>(obj, query : Expr<'t>, value : 't) =
match query with
| PropertyGet(a, b, list) ->
b.SetValue(obj, value)
| _ -> ()
let GetValue<'o, 't>(obj : 'o , query : Expr<'t>) : option<'t> =
match query with
| PropertyGet(a, b, list) ->
option.Some(b.GetValue(obj) :?> 't )
| _ -> option.None
let Observe<'t>(x: INotifyPropertyChanged) (p : Expr<'t>) =
let name = ToName(p)
x.PropertyChanged.
Where(fun (v:PropertyChangedEventArgs) -> v.PropertyName = name).
Select(fun v -> GetValue(x, p).Value)
type ViewModelBase() =
let propertyChanged = new Event<_, _>()
interface INotifyPropertyChanged with
[<CLIEvent>]
member x.PropertyChanged = propertyChanged.Publish
abstract member OnPropertyChanged: string -> unit
default x.OnPropertyChanged(propertyName : string) =
propertyChanged.Trigger(x, new PropertyChangedEventArgs(propertyName))
member x.SetValue<'t>(expr : Expr<'t>, v : 't) =
Property.SetValue(x, expr, v)
x.OnPropertyChanged(expr)
member x.OnPropertyChanged<'t>(expr : Expr<'t>) =
let propName = Property.ToName(expr)
x.OnPropertyChanged(propName)
However I get an error from the compiler
Error 1 The type 'ViewModelBase' is used in an invalid way.
A value prior to 'ViewModelBase' has an inferred type involving
'ViewModelBase', which is an invalid forward reference.
However the compiler doesn't tell me what value prior is the offending part of the problem. As I'm pretty new to the type inference as used by F# I'm probably missing an obvious problem.
FYI the code is meant to be used like the below but at the moment this code is commented out and the error is only pertaining to the core code above
type TestModel() as this =
inherit MVVM.ViewModelBase()
let mutable name = "hello"
let subscription = (Property.Observe this <# this.SelectedItem #>).
Subscribe(fun v -> Console.WriteLine "Yo")
member x.SelectedItem
with get() = name
and set(v) =
x.SetValue(<# x.SelectedItem #>, v)
I found it.
let SetValue<'t>(obj, query : Expr<'t>, value : 't) =
match query with
| PropertyGet(a, b, list) ->
b.SetValue(obj, value)
| _ -> ()
was under constrained. Should be
let SetValue<'t>(obj : Object, query : Expr<'t>, value : 't) =
match query with
| PropertyGet(a, b, list) ->
b.SetValue(obj, value)
| _ -> ()
I've been working on polishing up my JSON code for my ECMAScript runtime and I decided to run an experiment. The following str function has 4 logical steps which I've broken up into functions and marked them inline.
and private str (state:StringifyState) (key:string) (holder:IObject) : IDynamic =
let inline fourth (value:IDynamic) =
match value.TypeCode with
| LanguageTypeCode.Null ->
state.environment.CreateString "null" :> IDynamic
| LanguageTypeCode.Boolean ->
let v = value :?> IBoolean
state.environment.CreateString (if v.BaseValue then "true" else "false") :> IDynamic
| LanguageTypeCode.String ->
let v = value :?> IString
state.environment.CreateString (quote v.BaseValue) :> IDynamic
| LanguageTypeCode.Number ->
let v = value :?> INumber
if not (Double.IsInfinity(v.BaseValue))
then v.ConvertToString() :> IDynamic
else state.environment.CreateString "null" :> IDynamic
| LanguageTypeCode.Object ->
let v = value :?> IObject
let v = if v.Class = "Array" then ja state v else jo state v
state.environment.CreateString v :> IDynamic
| _ ->
state.environment.Undefined :> IDynamic
let inline third (value:IDynamic) =
match value.TypeCode with
| LanguageTypeCode.Object ->
let v = value :?> IObject
match v.Class with
| "Number" ->
fourth (v.ConvertToNumber())
| "String" ->
fourth (v.ConvertToString())
| "Boolean" ->
fourth (v.ConvertToBoolean())
| _ ->
fourth value
| _ ->
fourth value
let inline second (value:IDynamic) =
match state.replacerFunction with
| :? ICallable as f ->
let args = state.environment.CreateArgs ([| state.environment.CreateString key :> IDynamic; value |])
let value = f.Call (state.environment, holder :> IDynamic, args)
third value
| _ ->
third value
let inline first (value:IDynamic) =
match value with
| :? IObject as v ->
let toJSON = v.Get "toJSON"
match toJSON with
| :? ICallable as f ->
let args = state.environment.CreateArgs ([| state.environment.CreateString key :> IDynamic |])
let value = f.Call (state.environment, value, args)
second value
| _ ->
second value
| _ ->
second value
first (holder.Get key)
I compiled with full optimizations and opened up the resulting assembly with Reflector to see the results.
[CompilationArgumentCounts(new int[] { 1, 1, 1 })]
internal static IDynamic str(StringifyState state, string key, IObject holder)
{
IObject obj3;
ICallable callable;
ICallable callable2;
IArgs args;
IDynamic dynamic3;
IDynamic dynamic4;
ICallable callable3;
IDynamic dynamic5;
IBoolean flag;
IString str;
INumber number;
IObject obj4;
string str2;
INumber number2;
IObject obj5;
string str3;
IString str4;
IBoolean flag2;
IDynamic thisBinding = holder.Get(key);
IObject obj2 = thisBinding as IObject;
if (obj2 == null)
{
callable = state.replacerFunction# as ICallable;
if (callable == null)
{
switch (thisBinding.TypeCode)
{
case LanguageTypeCode.Object:
obj3 = (IObject) thisBinding;
str2 = obj3.Class;
if (!string.Equals(str2, "Number"))
{
if (string.Equals(str2, "String"))
{
dynamic3 = obj3.ConvertToString();
switch (dynamic3.TypeCode)
{
case LanguageTypeCode.Null:
return (IDynamic) state.environment#.CreateString("null");
case LanguageTypeCode.Boolean:
flag = (IBoolean) dynamic3;
return (IDynamic) state.environment#.CreateString(!flag.BaseValue ? "false" : "true");
case LanguageTypeCode.String:
str4 = (IString) dynamic3;
return (IDynamic) state.environment#.CreateString(quote(str4.BaseValue));
case LanguageTypeCode.Number:
number = (INumber) dynamic3;
if (double.IsInfinity(number.BaseValue))
{
return (IDynamic) state.environment#.CreateString("null");
}
return (IDynamic) number.ConvertToString();
// ... I removed a large amount of code.
return (IDynamic) state.environment#.Undefined;
}
Clearly the inline modifier is quite literal. The code is quite huge and with some preliminary tests is very efficient. One might consider throwing inline on all of their functions if they didn't care about the size of the resulting assemblies. What are some guidelines I can follow to know when the use of inline is appropriate? If possible I would like to avoid having to measure performance every single time to determine this.
If you are using inline solely for performance considerations, then I think that all of the typical performance-related advice applies. Most importantly, set a performance target and profile your application for hotspots. Then use inline if you have reason to believe that it will improve performance, and test to verify that it does. Keep in mind that the IL that the F# compiler generates is JIT compiled anyway, so small functions (in terms of IL size) may be inlined in the compilation to machine code even if you don't use inline in your F# code.
I typically only use inline when I want to use statically resolved type variables (e.g. because of member constraints).
I agree with kvb's answer, but here are two specific reasons not to
consider throwing inline on all of their functions if they didn't care about the size of the resulting assemblies.
The obvious case is that inlining anonymous functions won't work.
More inlining (especially of big functions) -> less (effectively) code fits into cache -> the program works slower.