Just started playing with F# and I want to create some mutable model inside my app to play with, using F# discriminated union instead of class hierarchy. However there seems no way to "downcast" a discriminated union and "match with" doesn't propagate mutability. What should I do?
type Foo = {
mutable x: int
}
type FooBar =
| Foo of Foo
| Bar
let f = {x = 2};
do f.x <- 3; //All ok
let fb = Foo {x = 1}
do match fb with
| Foo {x = x} -> x <- 2 //"This value is not mutable"
| Bar -> ()
your problem is, that you match/deconstruct fb into a new pattern/value x : int (that is not the same as the f.x at all!) which will of course be immutable (as bindings/values in F# are by default).
you probably see this much better if you don't give both (the value and the pattern) the same name:
> match fb with Foo { x = y } -> y;;
val it : int = 1
see you match x against y so y will get the value of x (but will not be mutable)
situation in C#
let's look at what would be a similar situation in C#
assuming you have a Foo class:
class Foo { public int x { get; set; } }
and a FooBar base-class with
class Foo2 : FooBar
{
public Foo Value { get; }
}
(I striped the ctors because I am to lazy - you get the idea)
now you would do this:
var fb = new Foo2(new Foo(1));
var x = fb.Value.x;
x := 2;
do you think that fb.Value.x will be 2 or 1? ;)
how to pattern-match / mutate
use
let fb = Foo {x = 1}
do match fb with
| Foo f -> f.x <- 2
| Bar -> ()
instead - this will deconstruct f out of fb and then you can set the mutable record-field f.x
But I would suggest not starting to learn F# by trying out how to use mutable values - try to learn using immutability as much as you can istead :)
Related
I'm trying to create a set of types which should have equality defined for comparison with each-other.
I got this far without a problem:
[<CustomEquality; NoComparison>]
type Foo = Foo of string
with
override x.Equals (y : obj) =
match y with
| :? Foo y' ->
match x with
| Foo x' -> transform x' = transform y'
| :? Bar y' ->
match x with
| Foo x' -> transform x' = transform y'
| _ -> false
// x.GetHashCode omitted for brevity
and [<CustomEquality; NoComparison>] Bar = Bar of String
// similar definitions here
However, this doesn't seem to let me compare them. For example, this code fails to compile:
let foos : Foo list = getFoos ()
let bar : Bar = getBar ()
foos |> List.exists (fun f -> f = bar)
complaining that a list of Bars was expected in place of foos : Foo list. If I change the anonymous function to (fun (f : Foo) -> f = bar), the compiler instead complains that it expected a Foo in place of bar : Bar.
Is there a way to implement cross-type equality like this? Does the requirement that they are all singly-cased type unions with the same underlying type make it easier?
Footnote: Yes, I realize this might not be a good idea, but I want to try it and see how well it would suit my needs. My ultimate goal is to create types that cannot be passed in place of each-other, but that still allow for equality comparison and without creating a wrapping type union - other suggestions are welcome :)
I start with two sets, abc and zxc. "unmodified" has the correct number (1) of items.
Set abc has two items, one of which is the unmodified item
let unmodified = Set.intersect abc zxc
let newAndModified = a - unmodified
I expect newAndModified to contain one item, but instead it has two. It appears to be an identical set to abc.
Even though my Set.intersect worked fine, other Set functions are not returning the right result and I believe there is something wrong with how I implemented CompareTo for this object.
type Bar =
{ Id : int
Name : string
}
[<CustomEquality; CustomComparison>]
type Foo =
{ Name : string
Id : int
Bars : Bar list
}
override x.Equals(y) =
match y with
| :? Foo as y -> x.Id.Equals y.Id
&& x.Name.Equals y.Name
&& x.Bars.Head.Name.Equals y.Bars.Head.Name
| _ -> invalidArg "y" "cannot compare object to one that is not a Foo"
member x.CompareTo (y: obj) =
match y with
| null -> nullArg "y"
| :? Foo as y -> x.CompareTo(y)
| _ -> invalidArg "y" "Must be an instance of Foo"
member x.CompareTo (y: Foo) =
match x.Equals y with
| true -> 0
| false -> -1
interface IComparable<Foo> with
member x.CompareTo y = x.CompareTo y
interface System.IComparable with
member x.CompareTo y =
match y with
| :? Foo as y -> (x:> IComparable<_>).CompareTo y
| _ -> invalidArg "y" "cannot compare values of different types"
Your CompareTo implementation is definitely not going to work here. You return 0 when two objects are equal (which is good), but if they are not equal, you always return -1. This means that the first one is smaller than the last one.
However, this is not going to work. If you have objects a and b, then your comparison is saying that a < b and b < a (which is breaking the requirements on the ordering relation).
The F# set requires the objects to be orderable, because it keeps the data in a balanced tree (where smaller elements are on the left and greater are on the right) - so with CompareTo that always returns -1, it ends up creating a tree that makes no sense.
Why do you want to implement custom comparison for the type? I suppose you're doing that to only consider some of the fields and ignore some others. There is actually a nice trick you can do in this case, which is to create a tuple with the fields you're interested and call the built-in hash and compare functions on them. For example:
compare (x.Id, x.Name, x.Bars.Head.Name)
(y.Id, y.Name, y.Bars.Head.Name)
This will use default F# comparison on the tuples, which is a correctly defined ordering relation.
Perhaps a silly question, but why does the return value from unbox appear (in my F# Interactive session) to be typed as obj instead of the concrete type int? As far as I can understand (trying to apply existing knowledge from C#) if it's typed as obj then it's still boxed. Example follows:
> (unbox<int> >> box<int>) 42;;
val it : obj = 42
> 42;;
val it : int = 42
Function composition (f >> g) v means g (f (v)), so you're actually calling box<int> at the end (and the call to unbox<int> is not necessary):
> box<int> (unbox<int> 42);;
val it : obj = 42
> box<int> 42;;
val it : obj = 42
The types are box : 'T -> obj and unbox : obj -> 'T, so the functions convert between boxed (objects) and value types (int). You can call unbox<int> 42, because F# automatically inserts conversion from int to obj when calling a function.
On a related note: such a method is actually quite useful. I use it to deal with "the type of an object expression is equal to the initial type" behavior.
let coerce value = (box >> unbox) value
type A = interface end
type B = interface end
let x =
{ new A
interface B }
let test (b:B) = printf "%A" b
test x //doesn't compile: x is type A (but still knows how to relax)
test (coerce x) //works just fine
From the MSDN documentation I understand that if Run is implemented it will be called automatically at the end of the computational expression. It says that:
builder.Run(builder.Delay(fun () -> {| cexpr |}))
will be generated for the computational expression. Run and/or Delay will be omitted if they are not defined in the workflow builder. I was expecting my ReaderBuilder to return a list of MyItem objects when Run is called automatically. So I do not understand why I'm getting a type mismatch error. The errors are generated by the return statement inside the ProcedureBuilder foo at the end of my code listing here. Could someone please explain what I'm misunderstanding about workflow builders and what I have implemented incorrectly?
I'm getting the following errors:
The type ''a list' is not compatible with the type 'ReaderBuilder'
Type constraint mismatch. The type 'a list is not compatible with type ReaderBuilder The type ''a list' is not compatible with the type 'ReaderBuilder'
open System
open System.Data
open System.Data.Common
open System.Configuration
let config = ConfigurationManager.ConnectionStrings.Item("db")
let factory = DbProviderFactories.GetFactory(config.ProviderName)
type Direction =
| In
| Out
| Ref
| Return
type dbType =
| Int32
| String of int
type ReaderBuilder(cmd) =
let mutable items = []
member x.Foo = 2
member x.YieldFrom item =
items <- item::items
item
member x.Run item =
items
type ProcBuilder(procedureName:string) =
let name = procedureName
let mutable parameters = []
let mutable cmd:DbCommand = null
let mutable data = []
member x.Command with get() = cmd
member x.CreateCommand() =
factory.CreateCommand()
member x.AddParameter(p:string*dbType*Direction) =
parameters <- p::parameters
member x.Bind(v,f) =
f v
member x.Reader = ReaderBuilder(cmd)
member x.Return(rBuilder:ReaderBuilder) =
data
let (?<-) (builder:ProcBuilder) (prop:string) (value:'t) =
builder.Command.Parameters.[prop].Value <- value
type MyItem() =
let mutable _a = 0
let mutable _b = String.Empty
let mutable _c = DateTime.Now
member x.a
with get() = _a
and set n = _a <- n
member x.b
with get() = _b
and set n = _b <- n
member x.c
with get() = _c
and set n = _c <- n
let proc name = ProcBuilder(name)
let (%) (builder:ProcBuilder) (p:string*dbType*Direction) =
builder.AddParameter(p)
builder
let (?) (r:DbDataReader) (s:string) = r.GetOrdinal(s)
let foo x y =
let foo = proc "foo" % ("x", Int32, In) % ("y", String(15), In)
foo?x <- x
foo?y <- y
foo {
do! foo?x <- x
do! foo?y <- y
return foo.Reader {
let item = MyItem()
item.a <- r.GetInt32("a")
item.b <- r.GetString("b")
item.c <- r.GetDateTime("c")
yield! item
}
}
The problem in your example is that the foo.Reader { ... } block has a return type MyItem list (because this is what the Run member of the ReaderBuilder type returns). However, the Return member of ProcBuilder expects an argument of type ReaderBuilder.
The data field of ReaderBuilder will be always an empty list, so this is also suspicious. I think you probably want to change the Return of ProcBuilder to take an argument MyItem list instead.
However, I think that using custom computation builder for database access doesn't really give you much advantage. You're not creating a "non-standard computation" in some sense. Instead, you probably just want a nice syntax for calling commands & reading data. Using the dynamic operator can make this quite elegant even without computation builders - I wrote an article about this some time ago.
How could nested pattern matching, such as the following example, be re-written so that None is specified only once? I think the Maybe monad solves this problem. Is there something similar in the F# core library? Or, is there an alternative approach?
match a with
| Some b ->
let c = b.SomeProperty
match c with
| Some d ->
let e = d.SomeProperty
//and so on...
| None -> ()
| None -> ()
you can solve this using built-in capabilities: Option.bind
type A =
member this.X : B option = Unchecked.defaultof<_>
and B =
member this.Y : С option = Unchecked.defaultof<_>
and С =
member this.Z : string option = Unchecked.defaultof<_>
let a : A = Unchecked.defaultof<_>
let v =
match
a.X
|> Option.bind (fun v -> v.Y)
|> Option.bind (fun v -> v.Z) with
| Some s -> s
| None -> "<none>"
Frankly, I doubt that introducing full-fledged 'maybe' implementation (via computation expressions) here can shorten the code.
EDIT: Dream mode - on
I think that version with Option.bind can be made smaller if F# has more lightweight syntax for the special case: lambda that refer to some member of its argument:
"123" |> fun s -> s.Length // current version
"123" |> #.Length // hypothetical syntax
This is how the sample can be rewritten in Nemerle that already has such capabilities:
using System;
using Nemerle.Utility; // for Accessor macro : generates property for given field
variant Option[T]
{
| Some {value : T}
| None
}
module OptionExtensions
{
public Bind[T, U](this o : Option[T], f : T -> Option[U]) : Option[U]
{
match(o)
{
| Option.Some(value) => f(value)
| Option.None => Option.None()
}
}
}
[Record] // Record macro: checks existing fields and creates constructor for its initialization
class A
{
[Accessor]
value : Option[A];
}
def print(_)
{
// shortened syntax for functions with body -> match over arguments
| Option.Some(_) => Console.WriteLine("value");
| Option.None => Console.WriteLine("none");
}
def x = A(Option.Some(A(Option.Some(A(Option.None())))));
print(x.Value.Bind(_.Value)); // "value"
print(x.Value.Bind(_.Value).Bind(_.Value)); // "none"
I like desco's answer; one should always favor built-in constructs. But FWIW, here's what a workflow version might look like (if I understand the problem correctly):
type CE () =
member this.Bind (v,f) =
match v with
| Some(x) -> f x
| None -> None
member this.Return v = v
type A (p:A option) =
member this.P
with get() = p
let f (aIn:A option) = CE () {
let! a = aIn
let! b = a.P
let! c = b.P
return c.P }
let x = f (Some(A(None)))
let y = f (Some(A(Some(A(Some(A(Some(A(None)))))))))
printfn "Your breakpoint here."
I don't suggest this, but you can also solve it with exception handling:
try
<code that just keeps dotting into option.Value with impunity>
with
| :? System.NullReferenceException -> "None"
I just wanted to point out the rough equivalence of exception-handling to the Maybe/Either monads or Option.bind. Typically prefer one of them to throwing and catching exceptions.
Using Option.maybe from FSharpx:
open FSharpx
type Pet = { Name: string; PreviousOwner: option<string> }
type Person = { Name: string; Pet: option<Pet> }
let pers = { Name = "Bob"; Pet = Some {Name = "Mr Burns"; PreviousOwner = Some "Susan"} }
Option.maybe {
let! pet = pers.Pet
let! prevOwner = pet.PreviousOwner
do printfn "%s was the previous owner of %s." prevOwner pet.Name
}
Output:
Susan was the previous owner of Mr Burns.
But, e.g. with this person instead there is just no output:
let pers = { Name = "Bob"; Pet = None }