In my opinion type classes are not really helpful without higher-kind generics abstraction. And F# does not have that, nor does .NET. In fact, I would probably be more happy with ML-style modules (and their flavor of higher kinds) than with type classes. If I were to design a better language than F# right now, I would go for OCaml-like core with modules (and therefore higher-kinded generics), and I would probably opt for the modules to be erased by abstract interpretation like MLton does. This would leave core ML which the compiler could then handle the same way as F#.
The problem with F# is the extra complexity of objects and subtyping. Treating F# extended with higher kinds and modules formally in presence of OO sounds like a real challenge. Again, if you ask me, I'd throw OO out..
I agree, if at some point the F# team decides to implement Typeclasses at CLR level or at least as an F# compiler feature, they should first upgrade the type system in order to support Higher Kinds.
However the technique I use still allowed me to define (kind-of) typeclasses of higher kind types like Monad, Category, etc.
But it's true that becomes a little bit tricky to figure out how to specify types for Haskell wrappers (i.e. Kleisli) with a "hat" type, where the original type is HK.
Adding HK types to the CLR is a popular request, but has been officially declined by MS.
Just looked at your code. Inlining is nice. However, here:
1 2 3 4 5
type Ap = Ap with static member (?<-) (f:option<_>, _Applicative:Ap, x:option<_>) = ap f x static member (?<-) (f:list<_> , _Applicative:Ap, x:list<_> ) = ap f x static member (?<-) (f:IO<_> , _Applicative:Ap, x ) = ap f x static member (?<-) (f:_ -> _ , _Applicative:Ap, g: _ -> _ ) = ap f g
Does not this mean that one can't add new instances without recompiling the whole project? That would be a deal-breaker for me at least. The point is code reuse: if instance set is fixed, you can't reuse nice functions like `sequence` for arbitrary monads.
You can add instances later, in another module or even another assembly, that's not a problem, except if you try to add an instance outside it's own type definition, see my previous answer about the orphan instances limitation.
That code could be perfectly split this way:
1 2 3 4 5
type Ap = Ap with static member (?<-) (f:option<_>, _Applicative:Ap, x:option<_>) = ap f x static member (?<-) (f:list<_> , _Applicative:Ap, x:list<_> ) = ap f x static member (?<-) (f:_ -> _ , _Applicative:Ap, g: _ -> _ ) = ap f g let inline (<*>) x y = x ? (Ap) <- y
in another module
1 2 3 4 5 6
type IO<'a> = IO of (unit->'a) with // ... implement Bind and Return static member (?<-) (f:IO<_> , _Applicative:Ap, x ) = ap f x // ... // now you can use <*> with IO
But for option
, list
and (->)
you can't because they're already defined, so either you add the instances in the Ap definition or you do it later with a wrapper type (as I did with ZipList in that project).
See the example in the section Adding new instances to existing Typeclasses
In short: yes, you will be able to re-use sequence
without recompiling, as long as new operators as static member come into scope, because they will be included in the operator overloading resolution.
If you find errors while trying to do this please tell me, I will be interested to be able to reproduce it.
Nice! Did not realize this.
Could you explain what type classes would be like in F# without higher-kinded types? I always thought have the former required the latter. Would they be useful at all?
Also, do you know of any resources that compare the type class approach of Haskell to the module approach of Ocaml? I'd like to fully understand the trade-offs.
Thank you!
Without higher kinds type classes could be somewhat useful, since F#'s primary problem is lack of HK not TC I think we should look in that direction first.
In Haskell parlance, for example, without HK, Monad and even Functor classes are not definable. Num and Eq are definable. This can be useful of course in its own way. But I maintain that the biggest problem is lack of HK and therefore true monads. ML easily has true monads with modules.
There is a nice proposal to integrate TC into the ML module system that also compares TC and modules, see the Modular Type Classes paper:
[link:www.mpi-sws.org]
Disclaimer: I may sound like an expert but I am not (yet) one, I have not yet implemented either TC or even ML modules myself.
I have seen lots of people thinking about typeclasses for F# lately, and have wondered if it would be possible to implement it elegantly using macros (see my blog post). The good thing about macros that could help with typeclasses, is that you can have short and readable code, while the compiler actually does a lot of work. This means that the solution doesn't have to be simple, as long as a computer can generate code to make it work.
My suggestion for higher-order modules might help you/give you ideas.
I really want to see type-classes in .net but I think you have to support them at CLR-level to make them work
I would like to share my approach to Typeclasses in F#.
It doesn't work at CLR level but it's type-safe because it works at compile-time.
I use inline functions and operators (in fact just one) to avoid writing ugly "when" constraints.
Playing with this technique I was able to define most Typeclasses from Haskell including Monad Transformers.
I have a post here that explains the approach and a project at code.google that mimics Haskell Typeclasses using this technique, which I use sometimes as base library for my F# projects.
Your feedback is much appreciated.
This is a very interesting workaround indeed.
I played with it a little, and I found that the following also works:
1 2 3 4 5 6 7
#nowarn "64" type Fmap = Fmap with static member fmap (Fmap, x : option<'a>) = fun f -> Option.map f x static member fmap (Fmap, x : list<'a>) = fun f -> List.map f x let inline fmap f x = ((^a or ^b) : (static member fmap : ^a * ^b -> _) (Fmap, x)) f
It's a little more verbose and requires a nowarn
to compile flawlessly; but it avoids the need to use operators. This should remove the limitation of two polymorphic parameters, in case someone ever needs a typeclass with many parameters.
I believe it would also make it readable enough to have several methods in the same class, like this:
1 2 3 4 5 6 7 8 9
type Monad = Monad with static member return' (Monad, _ : 'a option) = fun (x:'a) -> Some x static member bind (Monad, x : 'a option) = fun f -> Option.bind f x let inline return' x = ((^a or ^b) : (static member return' : ^a * ^b -> _) (Monad, Unchecked.defaultof<'b>)) x let inline (>>=) x f = ((^a or ^b) : (static member bind : ^a * ^b -> _) (Monad, x)) f
Thanks a lot for your suggestion.
Indeed I'm spending lot of time trying to figure out how to get rid of the operators using techniques like this.
I would love to write code that way, I agree with you it's much more readable.
But while for basic stuff like bind and return works fine, when you get into Monad Transformers, Arrows things get more complicated and some static constraints get solved before hand.
I think the main problem is the inability to specify a triple or constraint, something like:
(^a or ^b or ^c).
The only way I found at the moment to generate it is using the ternary operator.
I will keep researching, more suggestions are welcome.
Feel free to clone the fsharp-typeclasses project and try to re-write the code in a better way.
This is one of the coolest workarounds I've seen. I played with this technique a bit but still haven't been able to make it extensible, as type extensions can't provide operator overloads. Example: [link:gist.github.com]
What am I missing?
BTW do you know any other F# programmers in Argentina? Maybe we could form a user group...
Thanks a lot for your feedback, I don't know any F# developer in Argentina (I live in Switzerland).
I'm not extending with Extension methods.
Extensions methods don't fit in this approach.
Maybe it's confusing, because the example I have in the post defines the type IO, then some variables, then it re-takes the IO type definition and finish defining the overloads for IO.
Despite it uses the same syntax as Extension Methods F# compiles them as normal methods because they are in the same module as the type definition.
The instance methods should be defined either in the Typeclass definition or in the Type definition.
In your code you don't include List in the Fmap type and you don't have the source code for List to add the overload there.
Basically what you' re trying to define is like an orphan instance in Haskell. I'm not sure if it's impossible to implement* but anyway even in Haskell is not a good practice.
Of course you can extend it with a wrapper type (i.e. ZipList), but instances for native types should be defined in the typeclass definition.
Have a look at the Script I have in the project with tests.
There you'll find samples with some new instance definitions for new types.
(*) Maybe there is a way to support orphan instances given that I was able to extend F# binary operators using the ternary operator.
I played a bit using this technique but still don't get the right static member constraints.
The main problem I face here is that F# don't compile when it finds more that one matching overload.
It will be fine if it just take the most specific definition, or even the first one and then issue a warning.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
open System type TypeClass<'a> = static member Instance = Unchecked.defaultof<'a> type TypeClassAttribute() = inherit Attribute() [<AbstractClass>] type Eq<'a when 'a :> IComparable<'a>> = member x.AreEqual (a: 'a) b = a.CompareTo b = 0 type Vector2D<'a when 'a:> IComparable<'a>>(x: 'a, y: 'a) = typeclass eq = Eq<'a> member this.X = x member this.Y = y member this.TypeClassEq(other: Vector2D<'a>) = AreEqual x other.X && AreEqual y other.Y override this.ToString() = "(" + x.ToString() + "; " + y.ToString() + ")" let a = Vector2D(1, 0) let b = Vector2D(1, 0) let c = Vector2D(0, 1) printfn "%A = %A is %b" a b (a.TypeClassEq b) printfn "%A = %A is %b" a c (a.TypeClassEq c) Console.ReadKey() |> ignore
This code now prints: (1; 0) = (1; 0)
is true(1; 0) = (0; 1)
is false
It uses stubs for TypeClass<'a>.Instance and TypeClassAttribute since I've not added their implementation to FSharp.Core.
It seems for me a bit strange that this code works, because eq should be null. Looks like F# emits call instruction to do an instance call (C# emits callvirt to check if this poiter is not null).
1 2 3
typeclass eq = Eq<'a> expands to [<TypeClass>] static let eq = TypeClass<eq<'a>>.Instance
and AreEqual is searched among eq members since it can't be resolved and typechecked itself.
This is just a proof of concept. I'm going to replace 'typeclass' with 'constraint' to preserve compatibility with old code, add instance member lookup and do something with the fact that in F# expression 'a + b' (+) has type 'a -> 'a -> 'a, but in C# it would has type 'a * 'a -> 'a. Finally, type class checking must be implemented at compile time.
Suggestions and critics are welcome.
I've added required classes to FSharp.Core, modified sources lies here: [link:bitbucket.org]
Here is an example source, note Arithmetics has default constructor and MathExt provides (+) operator for int type (resolution does not add it by now):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
[<AbstractClass>] type Arithmetics<'a> () = abstract member (+): 'a * 'a -> 'a [<TypeClassExtension>] type MathExt = static member (+)(a, b) = (+) a b type Vector2D<'a>(x: 'a, y: 'a) = typeclass ar = Arithmetics<'a> member this.X = x member this.Y = y override this.ToString() = "(" + x.ToString() + "; " + y.ToString() + ")" static member (+)(v1: Vector2D<'a>, v2: Vector2D<'a>) = let a = Vector2D(ar.(+)(v1.X, v2.X), (+) (v1.Y, v2.Y)) // 1 + 1 |> ignore a let a = Vector2D(1, 0) let b = Vector2D(-5, -14) printfn "%A + %A = " a b printfn "%A" (a+b)
Still no typeclass constraint check is done at compile time, just syntactic sugar.
Did you make changes to the compiler's code generation, or does it call into the reflection-based implementation that you posted at ntypeclasses.codeplex.com?
I think any useful type classes implementation needs to get around the problem of adding two numbers: that is, emitting the add opcode when adding two doubles or ints, and calling into the right op_Addition method everywhere else. This could work if you only referenced type classes implemented inside the same assembly (and let inline lets you go part of the way), but exposing enough information about type classes in the assembly metadata could be a lot of work.
I've not made working changes to the compiler yet. And I'm not currently going to use add opcode or F#'s inline because it would break compatibility with other .NET languages.
I've not tested NTypeClasses's performance yet. In this particular case an adequate JIT optimization would be way better than manual replacing method calls with an add instruction in F# compiler. I hope it has already been done by CLR team. In case it hasn't, we can always vote for it on Microsoft Connect.
Since TypeClass<t>.Instance refers to static readonly field it is possible in theory to inline all calls to its methods. This is also very important because in this case it is possible to replace virtual method calls with ordinary ones.
Topic tags
- f# × 3707
- websharper × 2884
- core × 418
- bolero × 329
- compiler × 291
- enhancement × 215
- functional × 201
- bug × 177
- ui next × 140
- ui × 132
- c# × 122
- classes × 97
- web × 97
- .net × 84
- book × 84
- async × 77
- ui.next × 67
- templates × 58
- website × 51
- trywebsharper × 50
- question × 46
- html × 45
- server × 45
- owin × 44
- javascript × 43
- parallel × 43
- parsing × 41
- testing × 41
- typescript × 39
- template × 38
- sitelet × 31
- asynchronous × 30
- feature request × 28
- monad × 28
- ocaml × 28
- warp × 28
- tutorial × 27
- haskell × 26
- dotnet-ws × 23
- linq × 22
- sitelets × 22
- workflows × 22
- rpc × 21
- getting started × 20
- wpf × 20
- fpish × 19
- introduction × 19
- silverlight × 19
- monodevelop × 17
- piglets × 17
- suave × 17
- docs × 16
- collections × 15
- jquery × 15
- proposal × 15
- aspnetcore × 14
- pipeline × 14
- reactive × 14
- 4.6.0.361 × 13
- documentation × 13
- kendoui × 13
- formlets × 12
- 4.1.0.171 × 11
- monads × 11
- released: v0.1 × 11
- websocket × 11
- 4.4.0.280 × 10
- 4.4.1.288 × 10
- opinion × 10
- tryfsharponwasm × 10
- 4.0.190.100-rc × 9
- deployment × 9
- fixed × 9
- in × 9
- json × 9
- plugin × 9
- scheme × 9
- solid × 9
- wontfix × 9
- 4.3.0.274 × 8
- 4.5.4.317 × 8
- basics × 8
- concurrent × 8
- highcharts × 8
- how-to × 8
- mvu × 8
- python × 8
- released: v0.11 × 8
- 4.1.1.175 × 7
- 4.5.1.304 × 7
- complexity × 7
- remoting × 7
- visual studio × 7
- 4.1.2.178 × 6
- 4.5.4.151 × 6
- authentication × 6
- datefns × 6
- lisp × 6
- real-world × 6
- released in 4.0.192.103-rc × 6
- resources × 6
- scala × 6
- websharper ui.next × 6
- workshop × 6
- xaml × 6
- 4.0.193.110 × 5
- 4.2.11.258 × 5
- 4.2.3.236 × 5
- aspnetmvc × 5
- azure × 5
- bootstrap × 5
- conference × 5
- css × 5
- dsl × 5
- formlet × 5
- java × 5
- list × 5
- metaprogramming × 5
- ml × 5
- q&a × 5
- released in Zafir.4.0.188.91-beta10 × 5
- released: v0.4 × 5
- released: v0.8 × 5
- spa × 5
- sql × 5
- visualstudio × 5
- websharper.forms × 5
- zafir × 5
- 4.0.192.106 × 4
- 4.0.195.127 × 4
- 4.1.0.38 × 4
- 4.2.1.86 × 4
- 4.2.13.263 × 4
- 4.2.6.118 × 4
- 4.5.5.155 × 4
- 4.6.4.404 × 4
- discussion × 4
- example × 4
- extension × 4
- extensions × 4
- fsi × 4
- fsx × 4
- help wanted × 4
- highlightjs × 4
- html5 × 4
- jqueryui × 4
- lift × 4
- performance × 4
- qna × 4
- react × 4
- reflection × 4
- released: v0.10 × 4
- released: v0.5 × 4
- remote × 4
- rest × 4
- teaching × 4
- todomvc × 4
- 4.0.196.147 × 3
- 4.1.0.34 × 3
- 4.1.6.207 × 3
- 4.2.1.223-beta × 3
- 4.2.14.264 × 3
- 4.2.4.114 × 3
- 4.2.4.247 × 3
- 4.2.5.115 × 3
- 4.2.6.253 × 3
- 4.2.9.256 × 3
- 4.5.0.140 × 3
- 4.5.0.290 × 3
- 4.5.18.348 × 3
- 4.5.2.309 × 3
- 4.5.8.327 × 3
- 4.6.2.386 × 3
- ajax × 3
- alt.net × 3
- aml × 3
- asp.net mvc × 3
- build × 3
- canvas × 3
- cloudsharper × 3
- compilation × 3
- d3 × 3
- data × 3
- database × 3
- erlang × 3
- events × 3
- file upload × 3
- forums × 3
- how to × 3
- http × 3
- inline × 3
- issue × 3
- kendo × 3
- macro × 3
- materialui × 3
- mono × 3
- msbuild × 3
- mvc × 3
- pattern × 3
- piglet × 3
- released in Zafir.4.0.187.90-beta10 × 3
- released: v0.12 × 3
- released: v0.9 × 3
- svg × 3
- type provider × 3
- view × 3
- websharper4 × 3
- 4.1.1.64 × 2
- 4.1.5.203 × 2
- 4.1.7.232 × 2
- 4.2.10.257 × 2
- 4.2.3.111 × 2
- 4.2.5.249 × 2
- 4.3.0.127 × 2
- 4.3.1.275 × 2
- 4.5.10.166 × 2
- 4.5.10.332 × 2
- 4.5.15.342 × 2
- 4.5.19.349 × 2
- 4.5.3.146 × 2
- 4.5.9.301 × 2
- android × 2
- api × 2
- asp.net × 2
- beginner × 2
- blog × 2
- chart × 2
- client × 2
- client server app × 2
- clojure × 2
- computation expressions × 2
- constructor × 2
- corporate × 2
- courses × 2
- cufp × 2
- debugging × 2
- direct × 2
- discriminated union × 2
- dom × 2
- elm × 2
- endpoint × 2
- endpoints × 2
- enterprise × 2
- entity framework × 2
- event × 2
- f# interactive × 2
- fable × 2
- flowlet × 2
- formdata × 2
- forms × 2
- fsc × 2
- fsharp × 2
- google × 2
- google maps × 2
- hosting × 2
- https × 2
- iis 8.0 × 2
- install × 2
- interactive × 2
- interface × 2
- iphone × 2
- iteratee × 2
- jobs × 2
- jquery mobile × 2
- keynote × 2
- lens × 2
- lenses × 2
- linux × 2
- listmodel × 2
- mac × 2
- maps × 2
- numeric × 2
- oauth × 2
- obfuscation × 2
- offline × 2
- oop × 2
- osx × 2
- packaging × 2
- pattern matching × 2
- pipelines × 2
- post × 2
- quotation × 2
- reference × 2
- released in Zafir.4.0.185.88-beta10 × 2
- released: v0.13 × 2
- released: v0.6 × 2
- remarkable × 2
- rx × 2
- script × 2
- security × 2
- self host × 2
- seq × 2
- sockets × 2
- stm × 2
- sweetalert × 2
- tcp × 2
- trie × 2
- tutorials × 2
- type × 2
- url × 2
- var × 2
- websharper.charting × 2
- websockets × 2
- wig × 2
- xna × 2
- zh × 2
- .net framework × 1
- .net interop × 1
- 2012 × 1
- 4.0.194.126 × 1
- 4.1.3.184 × 1
- 4.1.4.189 × 1
- 4.2.0.214-beta × 1
- 4.2.12.259 × 1
- 4.2.2.231-beta × 1
- 4.2.8.255 × 1
- 4.4.1.137 × 1
- 4.5.1.141 × 1
- 4.5.11.334 × 1
- 4.5.12.177 × 1
- 4.5.13.318 × 1
- 4.5.13.338 × 1
- 4.5.16.344 × 1
- 4.5.2.145 × 1
- 4.5.3.144 × 1
- 4.5.3.310 × 1
- 4.5.5.319 × 1
- 4.5.6.156 × 1
- 4.5.6.320 × 1
- 4.5.7.322 × 1
- 4.5.8.161 × 1
- 4.5.9.164 × 1
- 4.6.1.127 × 1
- 4.6.1.381 × 1
- 4.6.3.388 × 1
- 4.6.5.406 × 1
- 4.6.6.407 × 1
- Canvas Sample Example × 1
- DynamicStyle Animated Style × 1
- ES8 × 1
- Fixed in 4.0.190.100-rc × 1
- Metro-Ui-Css × 1
- Metro4 × 1
- Released in Zafir.UI.Next.4.0.169.79-beta10 × 1
- SvgDynamicAttribute × 1
- Swiper × 1
- WebComponent × 1
- WebSharper.TypeScript × 1
- abstract class × 1
- accumulator × 1
- active pattern × 1
- actor × 1
- addin × 1
- agents × 1
- aggregation × 1
- agile × 1
- alter session × 1
- animation × 1
- anonymous object × 1
- apache × 1
- appcelerator × 1
- architecture × 1
- array × 1
- arrays × 1
- asp.net 4.5 × 1
- asp.net core × 1
- asp.net integration × 1
- asp.net mvc 4 × 1
- asp.net web api × 1
- aspnet × 1
- ast × 1
- attributes × 1
- authorization × 1
- b-tree × 1
- back button × 1
- badimageformatexception × 1
- bash script × 1
- batching × 1
- binding-vars × 1
- bistro × 1
- body × 1
- bundle × 1
- camtasia studio × 1
- cas protocol × 1
- charts × 1
- clarity × 1
- class × 1
- cli × 1
- clipboard × 1
- clojurescript × 1
- closures × 1
- cloud × 1
- cms × 1
- code-review × 1
- coding diacritics × 1
- color highlighting × 1
- color zones × 1
- combinator × 1
- combinators × 1
- compile × 1
- compile code on server × 1
- config × 1
- confirm × 1
- content × 1
- context × 1
- context.usersession × 1
- continuation-passing style × 1
- coords × 1
- cordova × 1
- cors × 1
- coursera × 1
- cross-domain × 1
- crypto × 1
- crypto recovery × 1
- csla × 1
- current_schema × 1
- custom content × 1
- data grid × 1
- date × 1
- datetime × 1
- debug × 1
- declarative × 1
- delete × 1
- devexpress × 1
- dhtmlx × 1
- dictionary × 1
- directattribute × 1
- disqus × 1
- distance × 1
- do binding × 1
- doc elt ui.next upgrade × 1
- docker × 1
- dojo × 1
- dol × 1
- domain × 1
- dotnet core × 1
- du × 1
- duf-101 × 1
- dynamic × 1
- eastern language × 1
- eclipse × 1
- edsl × 1
- em algorithm × 1
- emacs × 1
- emotion × 1
- enums × 1
- error × 1
- etw × 1
- euclidean × 1
- eventhandlerlist × 1
- examples × 1
- ext js × 1
- extension methods × 1
- extjs × 1
- extra × 1
- facet pattern × 1
- failed to translate × 1
- fake × 1
- fantomas × 1
- fear × 1
- float × 1
- form × 1
- form-data × 1
- forum × 1
- fp × 1
- frank × 1
- fsdoc × 1
- fsharp.core × 1
- fsharp.powerpack × 1
- fsharpx × 1
- fsunit × 1
- function × 1
- functional style × 1
- funds × 1
- game × 1
- games × 1
- gc × 1
- generic × 1
- geometry × 1
- getlastwin32error × 1
- getting-started × 1
- good first issue × 1
- google visualization timeline × 1
- google.maps × 1
- grid × 1
- group × 1
- guide × 1
- hash × 1
- headers × 1
- hello world example × 1
- heroku × 1
- highchart × 1
- history × 1
- html-templating × 1
- http405 × 1
- httpcontext × 1
- hubfs × 1
- i18n × 1
- ide × 1
- ie 8 × 1
- if-doc × 1
- iis × 1
- image × 1
- images × 1
- inheritance × 1
- initialize × 1
- input × 1
- install "visual studio" × 1
- installer × 1
- int64 × 1
- interfaces × 1
- internet explorer × 1
- interop × 1
- interpreter × 1
- invalid × 1
- io × 1
- iobservable × 1
- ios × 1
- iot × 1
- ipad × 1
- isomorphic × 1
- javascript optimization × 1
- javascript semanticui resources × 1
- jquery-plugin × 1
- jquery-ui × 1
- jquery-ui-datepicker × 1
- jquerymobile × 1
- js × 1
- kendo datasource × 1
- kendochart × 1
- kendoui compiler × 1
- knockout × 1
- l10n × 1
- leaflet × 1
- learning × 1
- library × 1
- libs × 1
- license × 1
- licensing × 1
- lineserieszonescfg × 1
- local setting × 1
- localization × 1
- logging × 1
- loop × 1
- macros × 1
- mailboxprocessor × 1
- mapping × 1
- markerclusterer × 1
- markup × 1
- marshal × 1
- math × 1
- mathjax × 1
- message × 1
- message passing × 1
- message-passing × 1
- meta × 1
- metro style × 1
- metro-ui × 1
- micro orm × 1
- minimum-requirements × 1
- mix × 1
- mobile installation × 1
- mod_mono × 1
- modal × 1
- module × 1
- money help × 1
- mouseevent × 1
- mouseposition × 1
- multidimensional × 1
- multiline × 1
- multithreading × 1
- mysql × 1
- mysqlclient × 1
- nancy × 1
- native × 1
- nested × 1
- nested loops × 1
- netstandard × 1
- node × 1
- nunit × 1
- object relation mapper × 1
- object-oriented × 1
- om × 1
- onboarding × 1
- onclick × 1
- optimization × 1
- option × 1
- orm × 1
- os x × 1
- output-path × 1
- override × 1
- paper × 1
- parameter × 1
- persistence × 1
- persistent data structure × 1
- phonegap × 1
- plotly × 1
- pola × 1
- powerpack × 1
- prefix tree × 1
- principle of least authority × 1
- privacy × 1
- private × 1
- profile × 1
- programming × 1
- project × 1
- project euler × 1
- projekt_feladat × 1
- protected × 1
- provider × 1
- proxy × 1
- ptvs × 1
- public × 1
- pure f# × 1
- purescript × 1
- quant × 1
- query sitelet × 1
- quotations × 1
- range × 1
- raphael × 1
- razor × 1
- rc × 1
- reactjs × 1
- real-time × 1
- recovery × 1
- ref × 1
- region × 1
- released in 4.0.190.100-rc × 1
- released: v0.2 × 1
- released: v0.3 × 1
- released: v0.7 × 1
- reporting × 1
- responsive design × 1
- rest api × 1
- rest sitelet × 1
- restful × 1
- round table × 1
- router × 1
- routing × 1
- rpc reverseproxy × 1
- runtime × 1
- sales × 1
- sample × 1
- sampleapp × 1
- scriptcs × 1
- scripting × 1
- search × 1
- self hosted × 1
- semanticui × 1
- sequence × 1
- serialisation × 1
- service × 1
- session-state × 1
- sharepoint × 1
- signals × 1
- sitelet website × 1
- sitelet.protect × 1
- sitlets × 1
- slickgrid × 1
- source code × 1
- sqlentityconnection × 1
- ssl × 1
- standards × 1
- static content × 1
- stickynotes × 1
- streamreader × 1
- stress × 1
- strong name × 1
- structures × 1
- submitbutton × 1
- subscribe × 1
- svg example html5 websharper.ui.next × 1
- system.datetime × 1
- system.reflection.targetinvocationexception × 1
- table storage × 1
- targets × 1
- tdd × 1
- template ClientServer × 1
- templates ui.next × 1
- templating × 1
- text parsing × 1
- three.js × 1
- time travel × 1
- tls × 1
- tooltip × 1
- tracing × 1
- tsunamiide × 1
- turkish × 1
- twitter-bootstrap × 1
- type erasure × 1
- type inference × 1
- type providers × 1
- type-providers × 1
- typeprovider × 1
- ui next forms × 1
- ui-next × 1
- ui.next jqueryui × 1
- ui.next charting × 1
- ui.next formlets × 1
- ui.next forms × 1
- ui.next suave visualstudio × 1
- ui.next templating × 1
- unicode × 1
- unittest client × 1
- up for grabs × 1
- upload × 1
- usersession × 1
- validation × 1
- vb × 1
- vb.net × 1
- vector × 1
- view.map × 1
- visal studio × 1
- visual f# × 1
- visual studio 11 × 1
- visual studio 2012 × 1
- visual studio code × 1
- visual studio shell × 1
- visualstudio-websharper × 1
- vs2017 compiler zafir × 1
- vsix × 1
- web api × 1
- web-scraping × 1
- webapi × 1
- webcomponents × 1
- webforms × 1
- webgl × 1
- webrtc × 1
- webshaper × 1
- websharper async × 1
- websharper codemirror × 1
- websharper f# google × 1
- websharper forms × 1
- websharper reactive × 1
- websharper rpc × 1
- websharper sitelets routing × 1
- websharper warp × 1
- websharper-interface-generator × 1
- websharper.chartsjs × 1
- websharper.com × 1
- websharper.exe × 1
- websharper.owin × 1
- websharper.ui.next × 1
- websharper.ui.next jquery × 1
- websockets iis × 1
- webspeech × 1
- why-websharper × 1
- windows 7 × 1
- windows 8 × 1
- windows-phone × 1
- winrt × 1
- www.grabbitmedia.com × 1
- xamarin × 1
- xml × 1
- yeoman × 1
- yield × 1
- zafir beta × 1
- zafir websharper4 × 1
- zarovizsga × 1
Copyright (c) 2011-2012 IntelliFactory. All rights reserved. Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy |
Built with WebSharper |
This post is mostly targeted F# team, but public debates are welcome.
Since F# source code is public domain, a community can take part in development process. I've already seen here some posts about type class feature. So, IMHO, it would be nice to bring it to F#.
Current state
After a couple of weeks rushing into a "type class feature in .NET" problem, I can now propose a way to make it possible. I've started with mapping type classes to OO model of CLR. Here is the result.
As you can see from docs, I've mapped type class to abstract generic class, and type class parameters to generic class parameters. Usage is listed in docs. Implementation uses reflection API.
To use a type class feature, you must access
NTypeClasses.TypeClass<YourTypeClass<arg1, arg2, ...>>.Instance
. This is the only object of dynamically generated type derived fromYourTypeClass<arg1, arg2, ...>
. For each virtual method in your type class (including get_PropertyName and set_PropertyName methods generated from properties), implementation is searched in arg1, arg2, ... classes and in all classes marked withNTypeClasses.TypeExtensionAttribute
. If one is found, the corresponding method inYourTypeClass<...>
is overriden with single call instruction that passes arguments to implementation method. If method is abstract, such an implementation must exist or you'll getTypeLoadException
.I've also made a program to check if all type arguments used in type classes have such an abstract methods implemented. This can be called on assembly after successful compilation to check that all type class usages is valid. So it is possible to check types after compilation in any .NET language.
Ok then, it seems I'm not very good in explanation, but I hope you got that.
To be done, integrating to F#
Now I'm willing to bring some syntactic sugar for this feature into F#. Here's an example of what I'm trying to make work:
Problems and things to discuss
The first problem is lack of documentation on how F# compiler works internally. Since its code is pretty large, it seems hard to find right places to insert new code, and what part of existing code could be reused in new code. I've put "when
TypeClassName<args>
" support into parser, so that it produces an AST node for this. There are some things to be done: write this constraint into class's info, inherit constraints from type class, fix method and operator resolution so that type class's members can be used, and check that all methods and properties are implemented with type class args with fully instantiated type class. Also, currently, there's a possibility in F# compiler to add a constraint totypar
, but this feature requires single constraint for multipletypar
s (though one can always add the same constraint instance for multipletypars
).The second problem is that current F# implementation does not support defining static read-only fields I've used in many cases for
NTypeClasses
implementation. So, I can't integrate the code intoFSharp.Core
as is.The third problem is compatibility with other .NET languages. There are some ways: integrate support code into
FSharp.Core
and require all other languages to reference it to use type classes, developFSharp.Core
integration in parallel withNTypeClasses
, and useNTypeClasses
from F# compiler.---
"I think this will never be possible in any .NET language" by Brian McNamara