-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Simulate higher-kinded polymorphism #175
Comments
Problem Statement https://robkuz.github.io/Higher-kinded-types-in-fsharp-Intro-Part-I/ and if you are interested also have a look to see Part II describing the solution in Haskell https://robkuz.github.io/HKTS-in-fsharp-Part-II-A-Short-Visit-To-Haskell-Land/ Please read the blog post before you proceed Now after reading you might conclude that Discussion
So what could I do (except introducing some defunctionalized HKTs)? The only way I see What was the reason to go F#? For me most certainly NOT. Having a slightly nicer syntax around OO constructs and some better type inference than C# doesn't cut the mustard. Now what can be done to improve my example? Assuming that HKTs will not come and assuming that nothing I have written will convince anybody who has not been convinced long time ago as there are much better papers about the ins and outs of HKTs (and I think that both assumptions are sensible). Partial Solution I think this addition/extension/change should be unproblematic as this is from my experience (and some SO question I have seen) The the following code
Is really really hard to understand.
Whenever I do this I feel a bit like Dr. Strange mumbling some arcane magic spells. If there was a RankNTypes-feature the whole thing could
Expressing HKTs via type constraints(?)
It would be cool if this could be somehow expressed as
So enough of hopefully constructive input for today. |
Somehow I got preoccupied with idea of solving this with Traits (using experimental implementation from #243, so no HKT). Here is the result: https://gist.github.com/jindraivanek/ca09752df052d4f82b405f181f93f8b1. Few notes:
|
@jindraivanek looks interestingly! |
@robkuz good writeup! You can see new version here: https://gist.github.com/jindraivanek/ca09752df052d4f82b405f181f93f8b1 I quite like this solution, I think it is a good example of power of traits even without HKT :) |
@jindraivanek One drawback of your particular approach is however that you need to implement that interface which will not work if you dont have access to those types. Here is another approach (also typeclassifying the Brands). It's partly back to the very original problem stated in my blog. However that problem arose more to the fact that you manually have to dispatch the inj/prj functions and not so much of writing them type Brand<'tag, 'b>(value: obj) =
member this.Apply() : 'c = value :?> _
[<Trait>]
type IBrand<'Kind, 'Tag, 'a> =
abstract inj: 'Kind -> Brand<'Tag, 'a>
abstract prj: Brand<'Tag, 'a> -> 'Kind
type Decision<'a> = Decision of 'a
type InProgress = class end
type Finished = class end
type Cancelled = class end
type InProgress<'a> = {value: 'a; decision: Decision<'a>}
type Finished<'a> = {value: 'a; initial: 'a; timestamp: DateTime}
type Cancelled<'a> = {value: 'a; decisions: list<Decision<'a>>; timestamp: DateTime; reason: string}
[<Witness>]
type InProgressBrand<'a> =
interface IBrand<InProgress<'a>, InProgress,'a> with
member this.inj v = new Brand<_,_>(v)
member this.prj v = v.Apply()
[<Witness>]
type FinishedBrand<'a> =
interface IBrand<Finished<'a>,Finished, 'a> with
member this.inj v = new Brand<_,_>(v)
member this.prj v = v.Apply()
[<Witness>]
type CancelledBrand<'a> =
interface IBrand<Cancelled<'a>, Cancelled,'a> with
member this.inj v = new Brand<_,_>(v)
member this.prj v = v.Apply()
let inj x = x |> IBrand.inj
let prj x = x |> IBrand.prj Could you try this one out (I cant get that branch running on my machine)? It is slightly longer than yours yet still generic at the call site (or so I would expect) |
@robkuz Thanks :) Tagging interface works, I updated my code: https://gist.github.com/jindraivanek/ca09752df052d4f82b405f181f93f8b1 Thanks for pointing this out, I wasn't aware that write interface without member is possible. :) Yes, this compile. Btw. what's your problem with trait branch? I need to compile code using traits like this |
@jindraivanek cool. That looks nice. |
If at some day higher kinded types will be supported in C# (Source: HKT), then F# should support higher kinded types due to interoperability, too |
Why does it need CLR change, though? |
So I finally came across a scenario where I think HKTs would be useful -- in MVU apps. Here is the comment I posted in the F# Slack. I end up defining separate type trees for page models, page msgs, and page init args (aka routes). They are all the same basic structure with different data carried on the DU case, but I have to name the cases differently to avoid ambiguity. Just for further detail, the main update statement ends up looking like this: match model.Page, msg with
// top level messages
| _, Logout ->
...
| _, SwitchPage pageInit ->
...
// page specific
| HomeModel pageModel, HomeMsg pageMsg ->
let pageModel, effects = Home.update pageMsg pageModel
{ model with Page = HomeModel pageModel }, effects The main thing that HKTs would buy is not having to maintain 3 copies of the same structure, assuming they would work like I am thinking. | Home pageModel, Home pageMsg ->
... Maybe even opportunity to improve expressiveness. match Scope.merge model.Page msg with
| Home (pageModel, pageMsg) ->
... But I also wonder what tradeoffs this would imply. For example, to type inference. |
Here's another real world situation that would require Higher Kinds #892 these are frequently asked questions. Some hacks are available but no clean way to do it without HKs. |
Closing all |
Submitted by Daniel Fabian on 3/21/2014 12:00:00 AM
492 votes on UserVoice prior to migration
F# already has to make trade-offs when doing interop, e.g. it is possible to create a null value for a DU from C#, erased type providers don't work from anywhere but F# etc. Maybe F# could allow for higher-kinded polymorphism within F# code and use dynamic casts at runtime or maybe statically resolved inlining to simulate higher-kinded polymorphism.
Original UserVoice Submission
Archived Uservoice Comments
The text was updated successfully, but these errors were encountered: