What is the most idiomatic way in F # to deal with the following. Suppose I have a property that I want the type to satisfy, which does not make sense at the instance level, but ideally I would like to have access to the template against it?
To make this more specific, I defined an interface representing the concept of a ring (in the sense of abstract algebra). I would go for:
one.
and suppose I use it as follows:
type Integer = | Int of System.Numerics.BigInteger static member Zero with get() = Int 0I static member (+) (Int a, Int b) = Int (a+b) static member AsRing with get() = { new IRing1<_> with member __.Zero = Integer.Zero member __.Addition = Integer.(+) }
which allows me to write things like:
let ring = Integer.AsRing
which then allows me to make good use of the unit tests that I wrote to test the properties of the ring. However, I cannot match this correspondence.
2.
type IRing2<'a when 'a: equality> = abstract member Zero: 'a with get abstract member Addition: ('a*'a -> 'a) with get type Integer = | Int of System.Numerics.BigInteger static member Zero with get() = Int 0I static member (+) (Int a, Int b) = Int (a+b) interface IRing2<Integer> with member __.Zero = Integer.Zero member __.Addition with get() = Integer.(+)
which I can now match the image, but that also means that I can write nonsense such as
let ring = (Int 3) :> IRing2<_>
3.
I could use an extra level of indirection and basically define
type IConvertibleToRing<'a when 'a: equality> abstract member UnderlyingTypeAsRing : IRing3<'a> with get
and then basically build IRing3 <_> in the same way as in C # 1. This will let me write:
let ring = ((Int 3) :> IConvertibleToRing).UnderlyingTypeAsRing
which is detailed, but at least what I write is no longer read as nonsense. However, along with verbosity, an additional level of complexity does not actually โfeelโ justice here.
four.
I hadnโt fully thought about it yet, but I could just have an Integer type without implementing any interfaces, and then a module named Integer, allowing the associated values โโfor the Ring interfaces. I believe that then I could use reflection in a helper function that creates any IRing implementation for any type, where there is also a module with the same name (but with the module suffix in it with a compiled name)? This will combine the advantages of # 1 and # 2, I think, but I'm not sure if this is possible and / or too far-fetched?
Just for the background: just for this, I'm trying to implement my own mini algebraic algebra system (like Mathematica or Maple) in F #, and I decided that I came across enough algebraic structures to start introducing interfaces like IRing for unit testing , as well as (potentially) later for working with general operations on such algebraic structures.
I understand that part of what is here or not possible has more to do with the limitations on how this can be done in .NET and not in F #. If my intention is clear enough, I will be interested to know here how other functional languages โโwork around these kinds of design issues.