Generic limitation in F# - f#

Until now, I had a class like this one:
type C<'a when 'a :> A> (...)
But now I created a new type B:
type B (...) =
inherit A()
But I don't want C to support B, and this doesn't compile:
type C<'a when 'a :> A and not 'a :> B> (...)
How can I do that?

You can't and shouldn't. If B is an A, then C should handle it. If it's reasonable for C not to be able to handle B, then B shouldn't derive from A. Otherwise you're effectively breaking Liskov's Substitution Principle (or at least a variant of the same).
When you declare that B inherits from A, you're saying that it can be used as an A. If that's not the case, you shouldn't be using inheritance.

Related

Creating an F# object with a generic member

In F#, I have defined an interface with a generic member:
type MyType =
abstract MyMember<'a> : 'a -> int
I would like to write a function that can create an object that implements this interface:
module MyType =
let create f =
{
new MyType with
member __.MyMember(x) = f x
}
This currently fails to compile with the following error message:
This code is not sufficiently generic. The type variable 'a could not be generalized because it would escape its scope.
Is it possible to make this work via inlining and static resolved type parameters (or something similar)? Thanks.
An explanation for the compile error:
Object expressions construct anonymous classes - these are just regular classes, with a compiler generated name. You're essentially creating a closure which captures f.
This is how you'd normally implement it:
type MyType =
abstract MyMember<'a> : 'a -> int
type MyNewType (f) =
interface MyType with
member _.MyMember x = f x
The type's constructor would have a parameter f of type 'a -> int, where 'a would normally be decided by the first usage of MyNewType.
But there's no way to generalize f such that it could be captured as a value that would satisfy the generic constraint of MyMember. Here sufficiently generic means a type of one order above the generic type.

reusing type aliases in type aliases

So I was quite impressed the other day when I saw...
type Foo<'a> =
abstract foo : Unit -> 'a
type IsFoo<'a,'b when 'a :> Foo<'b>> = 'a
(I know this example is a bit daft)
the point being that complex parametric constraints can be captured in a type alias....nice
I then started using this a bit, but I got to a point where I have a function that requires a Bar
type Bar<'b> =
abstract bar : Unit -> 'b
and so we have
type IsBar<'a,'b when 'a :> Bar<'b>> = 'a
but then I wanted a Foo that returned a Bar....so I COULD go....(I think)
type IsFooThenBar<'a,'b,'c when 'a :> Foo<'b> and 'b :> Bar<'c>> =
IsFoo<'a,IsBar<'b,'c>>
but this is sort of just a restatement of stuff I already know...really I'd quite like the constraint to be inferred i.e.
type IsFooThenBar2<'a,'b,'c> = IsFoo<'a,IsBar<'b,'c>>
but that clearly doesnt work because the compiler complains I cant assert IsFoo and IsBar unless I have the required contraints...
any ideas?....I have lots of generic constraints on my code, and these don't really play nicely with type declarations.
to clarify based on some feedback, how can I take a set of type aliases and compose them into more complex ones without simply restating them....i.e. I want to be able to ammend one of the axiomatic aliases, and that be reflectied into derived aliases, without lots and lots and lots of typing.
so making this concrete, I can write
let f (x : IsFoo<_,IsBar<_,string>>) =
x.foo().bar()
which in simple cases is OK...but remember I have lots and lots of parameters...
so I really want to simplify this and write...
let f2 (x : IsFooThenBar<_,_,string>) =
x.foo().bar()
and I can of course do that if I define IsFooThenBar as described above....
i.e. type aliases give me the mechanism I want, but there seems to be no simple way to compose type aliases without restating all the constraints in the top level alias.
thats fine in this case....but for large numbers of parameters, it becomes more and more onerous.
So...an example of a language where you can do this sort of thing is Haskell...
> class Foo a b | a -> b where
> foo :: a -> b
>
> class Bar a b | a -> b where
> bar :: a -> b
> class (Foo foo bar,Bar bar a) => FooThenBar foo bar a where
I can then use FooThenBar as short hand for saying that there exists functions that map a 'foo to a 'bar and a 'bar to whatever type I specify
(I'm not Haskell expert and had to get help to create this loosely equivalent scenario)
this is the original function
> f :: (Bar bar b, Foo foo bar) => foo -> b
> f x = bar (foo x)
and this uses the new inferred composition
> f' :: (FooThenBar foo bar b) => foo -> b
> f' x = bar (foo x)

Type kind error when creating instance of Functor typeclass

I'm trying to implement Functor typeclass instance for a very trivial type Foo:
data Foo a = Foo a
instance functorFoo :: Functor (Foo a) where
map fn (Foo a) = Foo (fn a)
Purescript gives me not-so-helpful error message:
Could not match kind
Type -> Type
with kind
Type
What does it mean? I'm not yet really familiar with the Kind-system.
But I'd still like to find out why does the first version not work. Like how does the Functor typeclass differ from e.g. Semigroup where the first version worked just fine.
Let's look at the definition of the Functor type class:
class Functor f where
map :: forall a b. (a -> b) -> f a -> f b
It contains f a and f b types in the type signature of map which clearly indicates that f is of kind Type -> Type (it "carries" another type). On the other hand Semigroup clearly relates to plain types of kind Type:
class Semigroup a where
append :: a -> a -> a
So Semigroup is a class which can be defined for plain types like String, Int, List a, Map k v or even a function a -> b (which can also be written as (->) a b) but not type constructors which require another type like List Map k (->) a (I have to use this notation here).
On the other hand Functor class requires type constructors so you can have instances like Functor List, Functor (Map k) or Functor ((->) a).
I found a solution. Instead of defining it as:
instance functorFoo :: Functor (Foo a) where
I need to define it as:
instance functorFoo :: Functor Foo where
But I'd still like to find out why does the first version not work. Like how does the Functor typeclass differ from e.g. Semigroup where the first version worked just fine.

F#: Why can't I cons an object of type B to a list of type A when B is a subtype of A?

According to the 3rd example on this site http://msdn.microsoft.com/en-us/library/dd233224.aspx, F# lists can contain objects of different types as long as both types derive from the same super-type. However, I can't get the cons (::) operator to add subtypes to a list of the supertype
module test
type A() =
member this.x = 2
type B() =
inherit A()
member this.y = 4
let mutable myList : A list = []
myList <- [B()] // Valid
myList <- B()::myList // Invalid: This expression was expected to have type A but here has type B
Is there a reason I can't use :: to append to the list?
F# does not always insert upcasts (conversion to a base type) automatically, so you have to insert an explicit cast that turns the B value into a value of type A.
Note that F# distinguishes between upcasts - casts to a base class (which are always correct) and downcasts - casts to a derived class (which may fail).
You can either use the upcast keyword or you can use the expr :> Type notation. In both cases, the compiler can fill in the required target type, so you can just write:
myList <- (upcast B())::myList
myList <- (B() :> _)::myList

Is F# aware of its discriminated unions' compiled forms?

A discriminated union in F# is compiled to an abstract class and its options become nested concrete classes.
type DU = A | B
DU is abstract while DU.A and DU.B are concrete.
With ServiceStack, the serialization of types to JSON strings and back can be customized with functions. With respect to the DU type, here's how I could do it in C#.
using ServiceStack.Text;
JsConfig<DU.A>.SerializeFn = v => "A"; // Func<DU.A, String>
JsConfig<DU.B>.SerializeFn = v => "B"; // Func<DU.B, String>
JsConfig<DU>.DeserializeFn = s =>
if s == "A" then DU.NewA() else DU.NewB(); // Func<String, DU>
Is F# aware of its discriminated unions' compiled forms? How would I get the type of DU.A in F# at compile time?
typeof<DU> // compiles
typeof<DU.A> // error FS0039: The type 'A' is not defined
typeof<A> // error FS0039: The type 'A' is not defined
I can easily enough register a function for deserialization in F#.
open System
open ServiceStack.Text
JsConfig<DU>.RawDeserializeFn <-
Func<_, _>(fun s -> printfn "Hooked"; if s = "A" then A else B)
Is it possible to register serialize functions wholly in F# for the concrete types DU.A and DU.B?
Whilst all the behaviour (the abstract classes etc.) is not just an implemenation detail, it is actually defined by the spec, these things are not accesible from F# - this is a quote from the spec
A compiled union type U has:
· One CLI static getter property U.C for each null union case
C. This property gets a singleton object that represents each such
case.
· One CLI nested type U.C for each non-null union case C. This
type has instance properties Item1, Item2.... for each field of the
union case, or a single instance property Item if there is only one
field. However, a compiled union type that has only one case does not
have a nested type. Instead, the union type itself plays the role of
the case type.
· One CLI static method U.NewC for each non-null union case C.
This method constructs an object for that case.
· One CLI instance property U.IsC for each case C. This
property returns true or false for the case.
· One CLI instance property U.Tag for each case C. This
property fetches or computes an integer tag corresponding to the case.
· If U has more than one case, it has one CLI nested type
U.Tags. The U.Tags typecontains one integer literal for each case, in
increasing order starting from zero.
· A compiled union type has the methods that are required to
implement its auto-generated interfaces, in addition to any
user-defined properties or methods.
These methods and properties may not be used directly from F#.
However, these types have user-facing List.Empty, List.Cons,
Option.None, and Option.Some properties and/or methods.
Importantly, "these methods and properties may not be used from F#".
Daniel is correct, you can do this by registering serialization functions for the base type DU. Here is a fuller example
open System
open ServiceStack.Text
type DU = A | B
let serialize = function
| A -> "A"
| B -> "B"
let deserialize = function
| "A" -> A
| "B" -> B
| _ -> failwith "Can't deserialize"
JsConfig<DU>.SerializeFn <- Func<_,_>(serialize)
JsConfig<DU>.DeSerializeFn <- Func<_,_>(deserialize)
let value = [| A; B |]
let text = JsonSerializer.SerializeToString(value)
let newValue = JsonSerializer.DeserializeFromString<DU[]>(text)
Result:
val value : DU [] = [|A; B|]
val text : string = "["A","B"]"
val newValue : DU [] = [|A; B|]
The fact that a DU in F# is a single type is key to its usefulness. The F# approach would be to use pattern matching:
JsConfig<DU>.SerializeFn <- function
| A -> "A"
| B -> "B"
This should work because the union cases are not only nested types in C#, but subtypes as well. Of course if ServiceStack doesn't consider base type serializers then this won't work.

Resources