Partial instance of a class

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
21 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Partial instance of a class

Jeroen Bransen-2
Hi Haskell cafe,

Is there any standard or guideline for making a "partial" instance of a
class, by which I mean implementing some methods of a class, but not all
of them? In my concrete case I've got some type X which is almost an
Arrow, except that I cannot lift any function a -> b to my type (of
course I can for some a and b), so I cannot give a sensible
implementation for arr. I can however give sensible implementations for
the other methods in the Arrow class, and I'd like to use them (and
possibly derived combinators) in other places.

I see three possible solutions for this situation, I think I've seen at
least the first two being used in well-known packages, but I couldn't
find them anymore:

1. Make a class instance, and for the methods you don't implement put an
'error' call
2. Create functions with names from the class and with types specialised
to your type, so that you can use it as if the instance was there. I.e.
provide a function first :: X a b -> X (a, c) (b, c), which should be
imported qualified to avoid name conflicts with the real Arrow class.
The drawback of this is of course the conflicting names, but also the
fact that derived combinators cannot be used.
3. Create functions with different names, to also avoid name clashes,
i.e. firstX :: X a b -> X (a, c) (b, c).

My preference would be to pick 1., but it feels quite bad in the same
time. Any suggestions?

Jeroen
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Partial instance of a class

MarLinn
Hi,

Is there any standard or guideline for making a "partial" instance of a class, by which I mean implementing some methods of a class, but not all of them? In my concrete case I've got some type X which is almost an Arrow, except that I cannot lift any function a -> b to my type (of course I can for some a and b), so I cannot give a sensible implementation for arr. I can however give sensible implementations for the other methods in the Arrow class, and I'd like to use them (and possibly derived combinators) in other places.

this might not be the general answer you're looking for, but related to this special case:

Note that arrow syntax (both proc-do-notation and banana brackets) uses arr extensively under the hood. The same goes for many of the derived combinators. So you can not simply leave it out. But maybe there's no need to reinvent the wheel either. What you have probably is something like a profunctor or a braided category, so you might be in luck finding some better suited library instead. For the former, probably the profunctors library. For the latter, maybe the subhask prelude, but more likely the categories library. If nothing else looking at them might help you better understand what structure you're dealing with. I personally don't use subhask, but the hierarchy diagrams and code snippets alone have been enlightening in the past. They might give you new search terms on the hunt for a better library.

Or maybe it's enough to make it an Applicative?

As a sidenote, I've seen this exact problem mentioned several times. Many nice things are almost arrows without an arr. But the arrow machinery pre-dates our current hierarchies and understanding, and it's never really been revised. From a more modern point of view there's little reason for its prominent place in base, except as an honorary member. There has also been discussion if/how the notation might be generalized. But it seems the pain was never strong enough, no one knows of a golden way forward, and there's always been ways around this. Oh well.

Cheers,
MarLinn



_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Partial instance of a class

Rick Owens
I would also like to register interest in what the community thinks about this problem, and add a couple of more motivating examples that I face regularly in my work:

1) `Bounded`, for things that have a minBound but no maxBound (like String). I often resort to Default in this case to avoid the use of `error`, but am unhappy with this because it doesn't "mean" the same thing, nor can it use derived combinators, as Jeroen mentions.

2) `Enum`, when used with types with more than 2^64 values (i.e. a sha-256 derived "keyspace", with 2^256 values). The `fromEnum` documentation claims "It is implementation-dependent what fromEnum returns when applied to a value that is too large to fit in an Int", but since approximately zero percent of 2^256 values will fit in an Int, I typically resort to `error` for all cases just to make it clear that toEnum/fromEnum are not going to work for any meaningful fraction of possible values.

Thanks!

-Rick



On Mon, Feb 26, 2018 at 11:36 AM, MarLinn <[hidden email]> wrote:
Hi,

Is there any standard or guideline for making a "partial" instance of a class, by which I mean implementing some methods of a class, but not all of them? In my concrete case I've got some type X which is almost an Arrow, except that I cannot lift any function a -> b to my type (of course I can for some a and b), so I cannot give a sensible implementation for arr. I can however give sensible implementations for the other methods in the Arrow class, and I'd like to use them (and possibly derived combinators) in other places.

this might not be the general answer you're looking for, but related to this special case:

Note that arrow syntax (both proc-do-notation and banana brackets) uses arr extensively under the hood. The same goes for many of the derived combinators. So you can not simply leave it out. But maybe there's no need to reinvent the wheel either. What you have probably is something like a profunctor or a braided category, so you might be in luck finding some better suited library instead. For the former, probably the profunctors library. For the latter, maybe the subhask prelude, but more likely the categories library. If nothing else looking at them might help you better understand what structure you're dealing with. I personally don't use subhask, but the hierarchy diagrams and code snippets alone have been enlightening in the past. They might give you new search terms on the hunt for a better library.

Or maybe it's enough to make it an Applicative?

As a sidenote, I've seen this exact problem mentioned several times. Many nice things are almost arrows without an arr. But the arrow machinery pre-dates our current hierarchies and understanding, and it's never really been revised. From a more modern point of view there's little reason for its prominent place in base, except as an honorary member. There has also been discussion if/how the notation might be generalized. But it seems the pain was never strong enough, no one knows of a golden way forward, and there's always been ways around this. Oh well.

Cheers,
MarLinn



_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.


_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Partial instance of a class

Will Yager
To avoid the hellish nightmare of Java’s UnsupportedOperationException-riddled ecosystem, I think we should *very strongly* discourage partial implementations of typeclasses as much as humanly possible. If a type doesn’t fit the typeclass, please don’t pretend it does.

If it turns out that our typeclasses are divided along bad semantic boundaries (which they clearly are in some cases), we should fix that instead of turning the typeclass landscape into a minefield.

—Will
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Partial instance of a class

Clinton Mead
I’ve implemented a drop in replacement enum class here, which solves your issue among other things:


If it doesn’t have some of the instances you require feel free to send me a pull request.

On Tue, 27 Feb 2018 at 8:12 am, Will Yager <[hidden email]> wrote:
To avoid the hellish nightmare of Java’s UnsupportedOperationException-riddled ecosystem, I think we should *very strongly* discourage partial implementations of typeclasses as much as humanly possible. If a type doesn’t fit the typeclass, please don’t pretend it does.

If it turns out that our typeclasses are divided along bad semantic boundaries (which they clearly are in some cases), we should fix that instead of turning the typeclass landscape into a minefield.

—Will
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.

_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Partial instance of a class

Daniel Peebles
Also possibly interesting and relevant is Adam Megacz's fascinating (but sadly abandoned) work on generalized arrows and hetmet programming: http://www.megacz.com/berkeley/garrows/

On Mon, Feb 26, 2018 at 4:20 PM, Clinton Mead <[hidden email]> wrote:
I’ve implemented a drop in replacement enum class here, which solves your issue among other things:


If it doesn’t have some of the instances you require feel free to send me a pull request.

On Tue, 27 Feb 2018 at 8:12 am, Will Yager <[hidden email]> wrote:
To avoid the hellish nightmare of Java’s UnsupportedOperationException-riddled ecosystem, I think we should *very strongly* discourage partial implementations of typeclasses as much as humanly possible. If a type doesn’t fit the typeclass, please don’t pretend it does.

If it turns out that our typeclasses are divided along bad semantic boundaries (which they clearly are in some cases), we should fix that instead of turning the typeclass landscape into a minefield.

—Will
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.

_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.


_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Partial instance of a class

Clinton Mead
Also check out my Freelude package which allows for the definition of restricted arrows like you’ve mentioned:


On Tue, 27 Feb 2018 at 2:49 pm, Daniel Peebles <[hidden email]> wrote:
Also possibly interesting and relevant is Adam Megacz's fascinating (but sadly abandoned) work on generalized arrows and hetmet programming: http://www.megacz.com/berkeley/garrows/

On Mon, Feb 26, 2018 at 4:20 PM, Clinton Mead <[hidden email]> wrote:
I’ve implemented a drop in replacement enum class here, which solves your issue among other things:


If it doesn’t have some of the instances you require feel free to send me a pull request.

On Tue, 27 Feb 2018 at 8:12 am, Will Yager <[hidden email]> wrote:
To avoid the hellish nightmare of Java’s UnsupportedOperationException-riddled ecosystem, I think we should *very strongly* discourage partial implementations of typeclasses as much as humanly possible. If a type doesn’t fit the typeclass, please don’t pretend it does.

If it turns out that our typeclasses are divided along bad semantic boundaries (which they clearly are in some cases), we should fix that instead of turning the typeclass landscape into a minefield.

—Will
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.

_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.


_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Partial instance of a class

Jeroen Bransen-2
In reply to this post by MarLinn

Hi,

Is there any standard or guideline for making a "partial" instance of a class, by which I mean implementing some methods of a class, but not all of them? In my concrete case I've got some type X which is almost an Arrow, except that I cannot lift any function a -> b to my type (of course I can for some a and b), so I cannot give a sensible implementation for arr. I can however give sensible implementations for the other methods in the Arrow class, and I'd like to use them (and possibly derived combinators) in other places.

this might not be the general answer you're looking for, but related to this special case:

Note that arrow syntax (both proc-do-notation and banana brackets) uses arr extensively under the hood. The same goes for many of the derived combinators. So you can not simply leave it out. But maybe there's no need to reinvent the wheel either. What you have probably is something like a profunctor or a braided category, so you might be in luck finding some better suited library instead. For the former, probably the profunctors library. For the latter, maybe the subhask prelude, but more likely the categories library. If nothing else looking at them might help you better understand what structure you're dealing with. I personally don't use subhask, but the hierarchy diagrams and code snippets alone have been enlightening in the past. They might give you new search terms on the hunt for a better library.

Perfect. In my concrete case I think a profunctor is indeed the thing I was looking for, and based on the other replies it seems that in general there are usually other classes that do match the set of functions you'd like. This of course doesn't completely solve the problem of the desire to use certain combinators, but I do agree that using error to partially implement a class isn't satisfactory either, and is very likely to lead to more problems.

Jeroen

_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Partial instance of a class

Tom Ellis
In reply to this post by MarLinn
On Mon, Feb 26, 2018 at 06:36:27PM +0100, MarLinn wrote:
> >I've got some type X which is almost an Arrow, except that I cannot lift
> >any function a -> b to my type (of course I can for some a and b), so I
> >cannot give a sensible implementation for arr.  I can however give
> >sensible implementations for the other methods in the Arrow class, and
> >I'd like to use them (and possibly derived combinators) in other places.
>
[...]
> ... I've seen this exact problem mentioned several times.
> Many nice things are almost arrows without an arr. But the arrow
> machinery pre-dates our current hierarchies and understanding, and
> it's never really been revised.

There's a myth floating around that "Arrow is much less useful because it
forces you to implement arr".  In fact, Arrow without arr would be as
useless as Applicative without fmap.  In almost all situations where you are
stymied by arr a small redesign will solve the whole problem.  In fact, you
need to get into the realm of linear-types-like things before arr is too
much (and even then a *linear* arr would be fine).

I designed a library for constructing Postgres queries and it uses an Arrow
interface.

    https://hackage.haskell.org/package/opaleye-0.5.3.0/docs/Opaleye-Internal-QueryArr.html

Naturally there is no way to run an arbitrary Haskell function "in the
database".  This is not an impediment because everything that the database
acts on inside the arrow type (QueryArr) is wrapped in an abstract type
(Column).  This means the only way that arbitrary Haskell functions can be
used inside the arrow is as a sort of "partial compilation".  There is, in
effect, a staging restriction.  Haskell functions a -> b run at "query
compile time" and Postgres functions run at "query run time".

This observation was made in an ICFP '13 paper in the context of our beloved
monads so nothing that I'm saying here is particular to those inscrutable
arrows.

    http://www.cse.chalmers.se/~joels/writing/bb.pdf

I would be sad to think that Haskell programmers are avoiding using Arrow
because of this myth[1].  If Jeroen Bransen or anyone else would like more
details about this approach please get in touch either via this list or
personally.  I'm happy to help out.

Tom



[1] On the other hand, avoiding using Arrow because the type in question is
actually a Monad is a perfectly good reason.

_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Partial instance of a class

Tom Ellis
In reply to this post by Jeroen Bransen-2
On Tue, Feb 27, 2018 at 11:38:21AM +0100, Jeroen Bransen wrote:
> In my concrete case I think a profunctor is indeed the thing I was looking
> for

If you're willing to share your datatype I may be able to help determine
exactly what sort of thing it is.  I have extensively explored the place of
profunctors :)
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Partial instance of a class

Jeroen Bransen-2


>> In my concrete case I think a profunctor is indeed the thing I was looking
>> for
> If you're willing to share your datatype I may be able to help determine
> exactly what sort of thing it is.  I have extensively explored the place of
> profunctors :)
http://hackage.haskell.org/package/progress-reporting-1.1.0/docs/Control-Monad-Progress.html

It's a class of functions (that may run in some monadic context) for
which we can report progress. I can lift certain types of functions to
this space (those that construct items of a list, or those that report
their own progress in some way), and then these compose in several ways.
Obviously having the first and second combinators from arrows comes in
handy.
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Partial instance of a class

Brandon Allbery
In reply to this post by Tom Ellis
I was under the impression most programmers avoid Arrows because they've seen HXT. >.> Which really wants to be Applicative, not Arrow, so it's not the best example of Arrows out there.

On Tue, Feb 27, 2018 at 5:52 AM, Tom Ellis <[hidden email]> wrote:
On Mon, Feb 26, 2018 at 06:36:27PM +0100, MarLinn wrote:
> >I've got some type X which is almost an Arrow, except that I cannot lift
> >any function a -> b to my type (of course I can for some a and b), so I
> >cannot give a sensible implementation for arr.  I can however give
> >sensible implementations for the other methods in the Arrow class, and
> >I'd like to use them (and possibly derived combinators) in other places.
>
[...]
> ... I've seen this exact problem mentioned several times.
> Many nice things are almost arrows without an arr. But the arrow
> machinery pre-dates our current hierarchies and understanding, and
> it's never really been revised.

There's a myth floating around that "Arrow is much less useful because it
forces you to implement arr".  In fact, Arrow without arr would be as
useless as Applicative without fmap.  In almost all situations where you are
stymied by arr a small redesign will solve the whole problem.  In fact, you
need to get into the realm of linear-types-like things before arr is too
much (and even then a *linear* arr would be fine).

I designed a library for constructing Postgres queries and it uses an Arrow
interface.

    https://hackage.haskell.org/package/opaleye-0.5.3.0/docs/Opaleye-Internal-QueryArr.html

Naturally there is no way to run an arbitrary Haskell function "in the
database".  This is not an impediment because everything that the database
acts on inside the arrow type (QueryArr) is wrapped in an abstract type
(Column).  This means the only way that arbitrary Haskell functions can be
used inside the arrow is as a sort of "partial compilation".  There is, in
effect, a staging restriction.  Haskell functions a -> b run at "query
compile time" and Postgres functions run at "query run time".

This observation was made in an ICFP '13 paper in the context of our beloved
monads so nothing that I'm saying here is particular to those inscrutable
arrows.

    http://www.cse.chalmers.se/~joels/writing/bb.pdf

I would be sad to think that Haskell programmers are avoiding using Arrow
because of this myth[1].  If Jeroen Bransen or anyone else would like more
details about this approach please get in touch either via this list or
personally.  I'm happy to help out.

Tom



[1] On the other hand, avoiding using Arrow because the type in question is
actually a Monad is a perfectly good reason.

_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.



--
brandon s allbery kf8nh                               sine nomine associates
[hidden email]                                  [hidden email]
unix, openafs, kerberos, infrastructure, xmonad        http://sinenomine.net

_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Partial instance of a class

Tom Ellis
In reply to this post by Jeroen Bransen-2
On Tue, Feb 27, 2018 at 12:02:49PM +0100, Jeroen Bransen wrote:

> >>In my concrete case I think a profunctor is indeed the thing I was looking
> >>for
> >If you're willing to share your datatype I may be able to help determine
> >exactly what sort of thing it is.  I have extensively explored the place of
> >profunctors :)
> http://hackage.haskell.org/package/progress-reporting-1.1.0/docs/Control-Monad-Progress.html
>
> It's a class of functions (that may run in some monadic context) for
> which we can report progress. I can lift certain types of functions
> to this space (those that construct items of a list, or those that
> report their own progress in some way), and then these compose in
> several ways. Obviously having the first and second combinators from
> arrows comes in handy.

The type in question is WithProgress

    data WithProgress m a b where
      Id            :: WithProgress m a a
      WithProgressM :: ((Double -> m ()) -> a -> m b)           -> WithProgress m a b
      Combine       :: WithProgress m b c -> WithProgress m a b -> WithProgress m a c
      SetWeight     :: Double             -> WithProgress m a b -> WithProgress m a b

Is this definition of arr somehow unsatisfactory?

    arr :: Applicative m => (a -> b) -> WithProgress m a b
    arr f = WithProgressM (\_ a -> pure (f a))

NB that if you implement Category and Profunctor then you automatically get
arr (\f -> lmap f id).

Tom
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Partial instance of a class

Tom Ellis
On Tue, Feb 27, 2018 at 11:21:51AM +0000, Tom Ellis wrote:
> NB that if you implement Category and Profunctor then you automatically get
> arr (\f -> lmap f id).

Or perhaps put more simply, you have no hope of making something an Arrow if
you can't make it a Functor, but if you *do* have Functor and Category then
you automatically get

    arr f = fmap f id

Tom
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Partial instance of a class

MarLinn
In reply to this post by Tom Ellis
Hi,

There's a myth floating around that "Arrow is much less useful because it
forces you to implement arr".  In fact, Arrow without arr would be as
useless as Applicative without fmap.  In almost all situations where you are
stymied by arr a small redesign will solve the whole problem.  In fact, you
need to get into the realm of linear-types-like things before arr is too
much (and even then a *linear* arr would be fine).

I designed a library for constructing Postgres queries and it uses an Arrow
interface.

    https://hackage.haskell.org/package/opaleye-0.5.3.0/docs/Opaleye-Internal-QueryArr.html

Naturally there is no way to run an arbitrary Haskell function "in the
database".  This is not an impediment because everything that the database
acts on inside the arrow type (QueryArr) is wrapped in an abstract type
(Column).  This means the only way that arbitrary Haskell functions can be
used inside the arrow is as a sort of "partial compilation".  There is, in
effect, a staging restriction.  Haskell functions a -> b run at "query
compile time" and Postgres functions run at "query run time".

Hm. Interesting point. And a nice coincidence that you call Applicative without fmap "useless". I just recently saw one of those. It *did* feel like there might be a better structure, but I couldn't pin it down. Maybe your technique works in that context as well? Would you mind having a look? I'd like to have my eyes opened in that direction.

The structure in question are XML-Picklers, i.e. tools to convert to and from XML. The original types are from HXT, but for the purpose of discussion we can simplify them to

    data PU a = PU { appPickle   :: a -> XmlState -> XmlState                    -- turn a value into XML
                   , appUnPickle :: XmlState -> (Either UnpickleErr a, XmlState) -- turn XML into a value
                   }
    
    -- "pure"
    xpLift :: a -> PU a
    xpLift x =  PU { appPickle   = const id
                   , appUnPickle = pure x
                   }    
   
    -- Combine two picklers sequentially
    -- If the first fails during unpickling, the whole unpickler fails
    xpSeq :: (b -> a) -> PU a -> (a -> PU b) -> PU b
    xpSeq f pa k
       = PU { appPickle   = ( \ b -> let a = f b in appPickle pa a . appPickle (k a) b )
            , appUnPickle = appUnPickle pa >>= (appUnPickle . k)    
            }

    -- Pickle a pair of values sequentially
    xpPair :: PU a -> PU b -> PU (a, b)
    xpPair pa pb = ( xpSeq fst pa (\ a -> xpSeq snd pb (\ b -> xpLift (a,b))) )

    -- The closest equivalent to "fmap"
    xpWrap :: (a -> b, b -> a) -> PU a -> PU b 

Now: This is not exactly an Applicative. If it where a functor, it would be a (lax) monoidal functor. Taking syntax from the Typeclassopedia, xpPair would be Monoidal's (**). And a (lax) monoidal functor is exactly an Applicative. If I'm not mistaken this structure satisfies all the laws of Monoidal – except that it is not a functor.

Obviously there's no way to implement fmap because you always need to provide functions for both directions, as seen in xpWrap. So how would you change this structure to make it possible?

It feels like the underlying problem is the same as with arr: At first there seems to be no way to lift functions into the structure. And we don't want to create two separate types because the whole idea of PU is to make pairs of related picklers and unpicklers composable.

Do I have a blind eye, nourished by that myth that often lifting is not possible? Or did I stumble upon that one usecase where there IS a useful Applicative-without-fmap?

MarLinn


_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Partial instance of a class

Tom Ellis
On Tue, Feb 27, 2018 at 02:28:31PM +0100, MarLinn wrote:

> >There's a myth floating around that "Arrow is much less useful because it
> >forces you to implement arr".  In fact, Arrow without arr would be as
> >useless as Applicative without fmap.
>
> a nice coincidence that you call Applicative without fmap "useless".  I
> just recently saw one of those.  It *did* feel like there might be a
> better structure, but I couldn't pin it down.  Maybe your technique works
> in that context as well?  ...
>
> The structure in question are XML-Picklers, i.e. tools to convert to
> and from XML. The original types are from HXT
>
>     <https://hackage.haskell.org/package/hxt-9.3.1.16/docs/src/Text-XML-HXT-Arrow-Pickle-Xml.html>
>
> Obviously there's no way to implement fmap because you always need
> to provide functions for both directions .... So how
> would you change this structure to make it possible?

Observe the definition from the HXT source

    data PU a       = PU { appPickle   :: Pickler a
                         , appUnPickle :: Unpickler a
                         , theSchema   :: Schema
                         }

type Pickler a          = a -> St -> St
newtype Unpickler a     = UP { runUP :: St -> (UnpickleVal a, St) }
type UnpickleVal a      = Either UnpickleErr a


Pickler is contravariant (Contravariant) and Unpickler is covariant
(Functor) so if we slighly augment PU we get a Profunctor.

    data PU' a b    = PU { appPickle   :: Pickler a
                         , appUnPickle :: Unpickler b
                         , theSchema   :: Schema
                         }

In fact Unpickler is a Monad (isomorphic to an EitherT of a State) so "PU' a"
is an Applicative and PU is what I call a "product profunctor".

    https://hackage.haskell.org/package/product-profunctors

This little augumentation in the definition of PU gets you a whole host of
free functionality from the libaries for profunctors, applicatives and
product profunctors.

(By the way, this observation is completely different from the "compiling
languages for different targets" technique that I mentioned in the previous
post.)

Tom
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Partial instance of a class

Tom Ellis
On Tue, Feb 27, 2018 at 01:46:49PM +0000, Tom Ellis wrote:
> In fact Unpickler is a Monad (isomorphic to an EitherT of a State) so "PU' a"
> is an Applicative and PU is what I call a "product profunctor".
>
>     https://hackage.haskell.org/package/product-profunctors

(Oh, we also need Pickler to be Data.Functor.Contravariant.Divisible, which
it indeed is.)
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Partial instance of a class

Michael Walker
In reply to this post by MarLinn
Hi,

On 27 February 2018 at 13:28, MarLinn <[hidden email]> wrote:
>
> It feels like the underlying problem is the same as with arr: At first there
> seems to be no way to lift functions into the structure. And we don't want
> to create two separate types because the whole idea of PU is to make pairs
> of related picklers and unpicklers composable.

For my own learning, a while ago I implemented a little
encoding/decoding library, with the encoders based on contravariant
functors.  I started with a type which I called `Codec a`, which
contained both an encoding and a decoding function.  As you did, I
quickly found that this makes many typeclass instances impossible.
So, like in Tom's reply, I instead added a more generic `Codec' a b`
type, which separates the encoding and decoding parameters.  I kept
`Codec a` as a type synonym for `Codec' a a`.

I found the change to make the resulting code much nicer to implement
and to use, even though it would be possible for a user to construct
an encoder/decoder pair which decodes to a different type than what
they started with.

In fact, after another round of type parameter introduction
(abstracting over the concrete type of the encoded value: bytestrings
and bytestring builders in `Codec'`, but type parameters in the yet
more general type), I discovered a very natural separation between my
"codecs" and combinators for composing them.

So in this case, I suppose the moral of the story is that having a
type parameter which is both covariant and contravariant is not that
useful, and you can always just constrain the type variables to be the
same where you need to anyway.

The code is here: https://github.com/barrucadu/wheat

--
Michael Walker (http://www.barrucadu.co.uk)
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Partial instance of a class

MarLinn
In reply to this post by Tom Ellis

> In fact Unpickler is a Monad (isomorphic to an EitherT of a State) so "PU' a"
> is an Applicative and PU is what I call a "product profunctor".
>
>      https://hackage.haskell.org/package/product-profunctors
>
> This little augumentation in the definition of PU gets you a whole host of
> free functionality from the libaries for profunctors, applicatives and
> product profunctors.
>
> (By the way, this observation is completely different from the "compiling
> languages for different targets" technique that I mentioned in the previous
> post.)
>

I did see that I could turn PU into a profunctor, but not that that
would make it an Applicative. Huh. Thanks for the tip!
(I may have been distracted by looking for an arr to turn that
profunctor into an arrow though.)

It seems that while the particular observation is quite different, the
more general approach is similar. Which nicely brings us back to the
original question: If something "almost" fits a class, there's probably
a better one around the corner. One might just be blind right now.

Cheers,
MarLinn

_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Partial instance of a class

Daniel Peebles
Not sure if everyone just missed my earlier link, but this talk of "Arrow without arr is useless" seems odd in the face of significant academic work (with Coq proofs, GHC extension, real-world use case for heterogeneous metaprogramming, etc.) going into exactly that abstraction.

Here it is again, if you did miss it: http://www.megacz.com/berkeley/garrows/


On Tue, Feb 27, 2018 at 9:19 AM, MarLinn <[hidden email]> wrote:

In fact Unpickler is a Monad (isomorphic to an EitherT of a State) so "PU' a"
is an Applicative and PU is what I call a "product profunctor".

     https://hackage.haskell.org/package/product-profunctors

This little augumentation in the definition of PU gets you a whole host of
free functionality from the libaries for profunctors, applicatives and
product profunctors.

(By the way, this observation is completely different from the "compiling
languages for different targets" technique that I mentioned in the previous
post.)


I did see that I could turn PU into a profunctor, but not that that would make it an Applicative. Huh. Thanks for the tip!
(I may have been distracted by looking for an arr to turn that profunctor into an arrow though.)

It seems that while the particular observation is quite different, the more general approach is similar. Which nicely brings us back to the original question: If something "almost" fits a class, there's probably a better one around the corner. One might just be blind right now.

Cheers,
MarLinn


_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.


_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
12