-
Notifications
You must be signed in to change notification settings - Fork 24
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
Attempt to provide some intuition for contravariant and divisible #33
Conversation
-- category of copresheaves from Hask to Hask, equipped with Day convolution mapping the | ||
-- Cartesian product of the source to the Cartesian product of the target. | ||
-- Continuing the intuition that 'Contravariant' functors consume input, a 'Divisible' | ||
-- contravariant functor also has the ability to composed "beside" another contravariant |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'the ability to be composed' I think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, thanks!
-- 'divide' is a generalization by also taking a 'contramap' like function to | ||
-- split any @a@ into a pair. This conveniently allows you to target fields of | ||
-- a record, for instance, by extracting the values under two fields and | ||
-- combining them into a tuple. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this lacks an example, so can I write serializeStringAndInt
using divide
, how?
src/Data/Functor/Contravariant.hs
Outdated
-- on a persons bank balance to work out if they are currently overdrawn: | ||
-- | ||
-- @ | ||
-- newtype Predicate a = Predicate (a -> Bool) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd define it as in the module
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
person's
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. Could you add links in @@
sections around contramap Contravariant etc.
This is sorely needed. |
src/Data/Functor/Contravariant.hs
Outdated
-- | ||
-- As an example, consider the type of predicate functions @a -> Bool@. One | ||
-- such predicate might be @negative x = x < 0@, which | ||
-- classisifies integers as to whether they are negative. However, given this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Classifies, I think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup
-- structure of the source category. (Here fst and snd are used in lieu of the more restricted | ||
-- lambda and rho, but this construction works with just a monoidal category.) | ||
-- That is, given a serializer for @a@ (@s :: Serializer a@), and a way to turn | ||
-- @b@s into @a@s (a mapping @f :: b -> a@), we have a serializer for @b@: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This sentence is what made it click for me, the only difficulty I had when reading this was that a
and b
are reversed compared to the type signature of contramap.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Specifically, this is what made Contravariant
click for me. 👍
Thanks a lot for this! That's the sort of thing we need more of. If I may ask: do you also understand |
I haven't decided to dive into that yet but it looks more approachable 👍
|
Feel free to use my blog post, of course! :) I just re-read it, and found the comments below. Oh my. :D |
I have no particular objection to including links to blog posts about the topic. |
@Blaisorblade yes, I'll do |
Ok, I've added an explanation for |
-- iBytes = runSerializer serializeB b | ||
-- in sBytes <> iBytes | ||
-- | ||
-- stringAndInt :: Serializer (String, Int) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
StringAndInt
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, type checker!
This looks nice overall. I have one gripe in that these changes move the laws for |
Agree, I think the laws should be front and center. I'll try and move them
…On Sun, 24 Sep 2017, 2:56 pm Ryan Scott ***@***.***> wrote:
This looks nice overall. I have one gripe in that these changes move the
laws for Decidable and Divisible away from their Haddocks and sequesters
them elsewhere. I'd prefer to keep the laws close to the classes
themselves, although I don't care if they're accompanied by the current
category theoretic jargon or not. I'd be OK with repeating the laws in the
additional section you've added, but explained in further detail.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#33 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AABRjhUvIaeMfjxZWZwAF3FWb29z00Bwks5sll9WgaJpZM4Pdxy0>
.
|
Is this still being worked on? If so, there's a pattern I've used in the past to make -- Right fixity, higher than >$<
infixr 5 >*<
(>*<) :: Divisible f => f a -> f b -> f (a, b)
(>*<) = divided Here's how you use it: data Person = Person
{ name :: String
, age :: Int
, living :: Bool
}
longName :: Predicate String
longName = Predicate (\name -> length name > 8)
old :: Predicate Int
old = Predicate (\age -> age > 60)
alive :: Predicate Bool
alive = Predicate id
oldAndAliveWithLongName :: Predicate Person
oldAndAliveWithLongName =
(\p -> (name p, (age p, living p)))
>$< longName
>*< old
>*< alive |
@mitchellwrosen I have to say I don't like that at all. Why not (<||>) :: Divisible f => f a -> f a -> f a
(<||>) = divide (\x -> (x, x))
oldAndAliveWithLongName :: Predicate Person
oldAndAliveWithLongName = contramap name longName
<||> contramap age old
<||> contramap living alive [Not type checked!] |
@tomjaguarpaw yours is cleaner, @mitchellwrosen's fits the Applicative analogy a bit better from a learning perspective |
It only feels like |
That's a good operator too, but it looks more like mempty = conquer
mappend = divide (\x -> (x, x)) (except that |
For EDIT or will they boil down to the same instance? |
Wouldn't the more natural/obvious |
Maybe, but that's just shifting the observation to "there are two choices of |
Of course, but it's odd to mix-and-match the two, no? Anywho, I feel like I
am spamming this issue unnecessarily :) Thanks for the discussion!
…On Oct 17, 2017 6:13 PM, "tomjaguarpaw" ***@***.***> wrote:
Maybe, but that's just shifting the observation to "there are two choices
of Divisible instance"!
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#33 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ABBlpvloNxFvyZSVDM52kUGCAoTOXby7ks5stSZxgaJpZM4Pdxy0>
.
|
Yes, this is being worked on, but the scope of this PR is to just improve documentation. All I need to do is add the laws, and I'm done. I'll happily take a patch from anyone else adding those laws, too. |
I'd say the |
Ah, we're just missing the Predicate one. Adding it. (Off topic digression ends) |
Merged what you have. We can do more intensive edits as we go. |
@ocharles you have commit access to the repo now, feel free to push further updates directly. |
Thanks guys! |
\o/ |
There was recently confusion (and, as I read it, frustration) on Twitter regarding
Contravariant
andDivisible
.Exhibit A - Will felt he had little more than a type signature to go on for
Contravariant
.Exhibit B - points out that 'Divisible' has nothing but a dense technical explanation. I posture that barely 1% of the people who use Haskell have even heard of a presheave, let alone understanding what the category Presheave even is.
I provided a few tweet replies, but this comes up time and time again. @tomjaguarpaw has explained 'Contravariant' once (https://ocharles.org.uk/blog/guest-posts/2013-12-21-24-days-of-hackage-contravariant.html), @phaazon spoke about divisible (https://phaazon.blogspot.co.uk/2015/08/contravariance-and-luminance-to-add.html), but I think it's about time we bought at least some of this into
contravariant
proper.Yes, the examples do not do justice to what a
Contravariant
functor truly is, but I believe they provide at least a gentle ramp to begin wading towards the deep end. I will understand if you feel this is the wrong place for this documentation.@jb55 this is written for you, so I'd appreciate knowing if it even helps. @acowley expressed not knowing what
Divisible
even was, so likewise feedback would be appreciated.