Typeclasses merge.

72 views
Skip to first unread message

Gustavo Leon

unread,
Dec 22, 2011, 6:19:39 AM12/22/11
to fsharpx
Hi all

I just finished implementing Typeclasses in Fsharpx in a generic way.
Have a look at the fork at https://github.com/gmpl/fsharpx

Breaking changes
----------------

- Monads defined on top of primitive types (Validation, Reader,
Writer, State, Cont and Distribution) are wrapped now as the standard
Haskell version.

- Cont changed to the standard version, without the additional
continuation for exceptions.

- Monadic functions and operators were removed from each Monad
definition.
Now they are defined once in Prelude, as generic functions, they work
on every monad. There's also a generic builder for monads : do'.

Mauricio Scheffer

unread,
Dec 22, 2011, 9:39:10 PM12/22/11
to fsh...@googlegroups.com
Looks very good! I'm going to try and use this in my code.
Something odd I noticed is there seemed to be an issue with seq  https://github.com/gmpl/fsharpx/commit/6b29d7384e8ff72abd4e0cf9e68edba6cb49fed7 Does it conflict with lists or something?

--
Mauricio

Gustavo Leon

unread,
Dec 23, 2011, 10:07:37 AM12/23/11
to fsharpx
Yes, it conflicts with list.
I think the main problem is that IEnumerable is an interface and
inheritance don't play well with Typeclasses.
Here's an example:

let f g x = g <*> x <*> [4;5]

Currently works and it guess the type as:

val f : ('a -> int -> 'b) list -> 'a list -> 'b list

but if we include seq we'll get

stdin(8,15): error FS0332: Could not resolve the ambiguity inherent in
the use of the operator '( ?<- )' at or near this program point.
Consider using type annotations to resolve the ambiguity.
> let f g x = g <*> x <*> [4;5] ;;

let f g x = g <*> x <*> [4;5] ;;

Because x could be a list or a seq, then the compiler is not able to
decide which implementation should take.

One thing we can do is (again) wrap seq into a concrete type, as we
did with ZipList, then we can define Functors and monads without
conflicts.


On Dec 23, 3:39 am, Mauricio Scheffer <mauricioschef...@gmail.com>
wrote:
> Looks very good! I'm going to try and use this in my code.
> Something odd I noticed is there seemed to be an issue with seqhttps://github.com/gmpl/fsharpx/commit/6b29d7384e8ff72abd4e0cf9e68edb...
> Does
> it conflict with lists or something?
>
> --
> Mauricio
>
> On Thu, Dec 22, 2011 at 8:19 AM, Gustavo Leon
> <gustavo.perezl...@gmail.com>wrote:
>
>
>
> > Hi all
>
> > I just finished implementing Typeclasses in Fsharpx in a generic way.
> > Have a look at the fork athttps://github.com/gmpl/fsharpx
>
> > Breaking changes
> > ----------------
>
> > - Monads defined on top of primitive types (Validation, Reader,
> > Writer, State, Cont and Distribution) are wrapped now as the standard
> > Haskell version.
>
> > - Cont changed to the standard version, without the additional
> > continuation for exceptions.
>
> > - Monadic functions and operators were removed from each Monad
> > definition.
> > Now they are defined once in Prelude, as generic functions, they work
> > on every monad. There's also a generic builder for monads : do'.- Hide quoted text -
>
> - Show quoted text -

Mauricio Scheffer

unread,
Jan 2, 2012, 4:05:46 PM1/2/12
to fsh...@googlegroups.com
I've been playing a bit with this. 

I renamed Validation Right|Left to Success|Failure:  https://github.com/mausch/fsharpx/commit/b579a098a4e943d17825d1a09e68d22e8f02b196 

Tried to simplify an expression in the validation tests and found the compiler needs a type annotation for some reason (not a big deal):  https://github.com/mausch/fsharpx/commit/e8fd8a2ab5d3158e1becb0bc551de82edaaf83da 

Tried to replace Choice.map with fmap in validation tests, for some reason it didn't work unless I broke down the expression (!). https://github.com/mausch/fsharpx/commit/4d2f760b3d1ef72b9b5af4a295fc5d167593553b
Does anyone have a clue about this?

Cheers,
Mauricio

Gustavo Pérez León

unread,
Jan 3, 2012, 2:49:29 AM1/3/12
to fsh...@googlegroups.com
Hi all

Unfortunately type inference doesn't work 100% I would rather say it works at 95%  depending on the context.
When I started this project I found many cases where type inference is unable to find the right type but in most cases I solved it using more type annotations at the input and output (with the defaultof< ^R> trick) parameters.
I think the main reason is because this is not "real" typeclasses, it's just overload with some (automatic) constraints.
Another thing I noted is that even when type inference works it makes compilation much slower in some cases, specially when do' notation is used.
It's a shame particularly in the case of Applicative Functors, I noted that type inference works better if the instances are defined at Prelude together with the "class" definition, that's why I defined Validation there, otherwise it's even worse.
I will keep researching what's the best way to overcome this limitation, in the meantime type annotations seems to be imperative in cases like this.

Kind regards

Gustavo

Ryan Riley

unread,
Mar 30, 2012, 2:11:57 PM3/30/12
to fsh...@googlegroups.com
This is a really nice approach. Honestly, the best possible solution would be for F# to support extension methods in its statically resolved type parameters. Then we could just attach a simple method like so:

    type Option<'a> with
        member x.Bind(f: 'a -> Option<'b>) = Option.bind f x

and on new types:

    type Identity<'a>(value) =
        static member Return x = Identity(value)
        member x.Bind(f: 'a -> Identity<'b>) = f value

The latter works really well with statically resolved type parameters:

    let inline (>>=) m f = (^M: (member Bind: ('a -> 'b) -> 'b) (m, f))
    Identity.Return 1 >>= fun a -> Identity.Return (a.ToString())

I'm not sure if this would work for monad transforms or even a singular computation expression. And besides, it doesn't work for existing types, which is unfortunate.

Ryan

Gustavo Pérez León

unread,
Apr 1, 2012, 8:50:46 AM4/1/12
to fsh...@googlegroups.com
Hi Ryan

Extension methods in statically resolved parameters would be great, they would allow to specify orphan instances, but I don't think they will be supported as extension methods is a .NET run-time feature while statically resolved parameters is an F# compiler feature, so they are resolved before.

Another thing to note is in your example you are defining "bind" as a member (not a static member) which will not work with existing types. Static members are almost like functions, they don't require instances.

In these days I'm going to upload a clone of my typeclasses project which uses no operators, but requires to fix a bug in the F# compiler (which I'm going to upload as well).
The good news are the F# Team told me that bug will be fixed in F# 3.0 so it will be possible to use it to compile, even for F# 2.0 code.
So I will invest more time studying this refinement of the technique, see if type inference could also be improved and try to find a way to specify orphan instances.

Gustavo

Ryan Riley

unread,
Sep 1, 2012, 1:29:45 PM9/1/12
to fsh...@googlegroups.com
The typeclasses project looks really good. What's the status on this? Do we want to pull this into F#x? Should it be a standalone project?

Mauricio Scheffer

unread,
Sep 1, 2012, 2:48:12 PM9/1/12
to fsh...@googlegroups.com
Gustavo changed the technique he was using in http://code.google.com/p/fsharp-typeclasses/ , as he mentioned before, it no longer uses operators. I believe this change requires the F# 3.0 compiler, so before switching to this new method we'd have to merge the VS2012 branch.
Also, I'd keep the current "explicit" module functions, e.g. Choice.map, Reader.map, etc, in addition to the truly generic fmap. This makes it possible to keep the reliable type signatures and type inference while at the same time allowing for typeclass power if you know what you're doing. (some examples in this context:  https://github.com/mausch/fsharpx/commit/eda97d65a90deeb6412b99241730669075819ab4#commitcomment-1613711   https://github.com/mausch/fsharpx/commit/4d2f760b3d1ef72b9b5af4a295fc5d167593553b#commitcomment-1613633  )

Cheers
Mauricio

Gustavo Pérez León

unread,
Sep 3, 2012, 7:26:14 AM9/3/12
to fsh...@googlegroups.com
Just to clarify, you can still compile the typeclasses project in F# 2 if you include the InlineHelper.dll which is the "core" of the overload resolution and it is already compiled in F# 3 (it's only about 20 lines of code).

Gustavo
Reply all
Reply to author
Forward
0 new messages