Example of non-trivial functorsAre all Haskell functors endofunctors?Concrete example showing that monads are not closed under composition (with proof)?Composition of two functors is a functorSets, Functors and Eq confusionAre Functor instances unique?Lax monoidal functors with a different monoidal structureWhat does a nontrivial comonoid look like?Haskell: Flaw in the description of applicative functor laws in the hackage Control.Applicative article?: it says Applicative determines FunctorIs this property of a functor stronger than a monad?Why does the Functor class in Haskell not include a function on objects? Is `pure` that function?
Can a human be transformed into a Mind Flayer?
Rail-to-rail op-amp only reaches 90% of VCC, works sometimes, not everytime
Can the removal of a duty-free sales trolley result in a measurable reduction in emissions?
Was Self-modifying-code possible just using BASIC?
How to write a convincing religious myth?
Should I put programming books I wrote a few years ago on my resume?
What is the logic behind charging tax _in the form of money_ for owning property when the property does not produce money?
What would prevent chimeras from reproducing with each other?
2019 gold coins to share
Reference to understand the notation of orbital charts
Does a bank have to tell me if a check made out to me was cashed there?
Why was this person allowed to become Grand Maester?
How do we say "within a kilometer radius spherically"?
What is the best color to differentiate male and female?
Why did the World Bank set the global poverty line at $1.90?
Why is long-term living in Almost-Earth causing severe health problems?
The usage of kelvin in formulas
Section numbering in binary
How to befriend someone who doesn't like to talk?
Is it okay to have a sequel start immediately after the end of the first book?
Who is "He that flies" in Lord of the Rings?
Who won a Game of Bar Dice?
Is there a DSLR/mirorless camera with minimal options like a classic, simple SLR?
Why did Intel abandon unified CPU cache?
Example of non-trivial functors
Are all Haskell functors endofunctors?Concrete example showing that monads are not closed under composition (with proof)?Composition of two functors is a functorSets, Functors and Eq confusionAre Functor instances unique?Lax monoidal functors with a different monoidal structureWhat does a nontrivial comonoid look like?Haskell: Flaw in the description of applicative functor laws in the hackage Control.Applicative article?: it says Applicative determines FunctorIs this property of a functor stronger than a monad?Why does the Functor class in Haskell not include a function on objects? Is `pure` that function?
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty height:90px;width:728px;box-sizing:border-box;
In Haskell, functors can almost always be derived, is there any case where a type is a functor and satisfies functor laws (such as fmap id == id
) but cannot be derived according to a simple set of rules?
And what about Foldable, Traversable, Semigroup and others? Is there any non-trivial cases available?
haskell functor category-theory
|
show 2 more comments
In Haskell, functors can almost always be derived, is there any case where a type is a functor and satisfies functor laws (such as fmap id == id
) but cannot be derived according to a simple set of rules?
And what about Foldable, Traversable, Semigroup and others? Is there any non-trivial cases available?
haskell functor category-theory
You mean something like tuple when you don't know should you apply function to first value or second?
– talex
Jun 3 at 6:43
@talex No, if you treat a tuple of (a, b) as a functor over b, then the result of fmap is (a, c), from which it's very clear which value shoud the function b -> c be applied to.
– Jiaming Lu
Jun 3 at 7:08
2
Functor in Haskell can always be derived. Bartosz Milewski talks about this in his book *Category Theory for Programmers*[1]. I need to look up the exact reference and details myself but it has to do with functors in Haskell forming some sort of an algebra. [1] bartoszmilewski.com/2014/10/28/…
– michid
Jun 3 at 7:20
5
Foldable, Traversable, and Semigroup all have symmetries which allow them to be lawfully instantiated in multiple distinct ways, which is one sense of nontriviality at least
– luqui
Jun 3 at 7:27
1
@luqui, if you use an Atkey-style indexed applicative to define a notion of traversable for type-aligned collections, then you should recover uniqueness.
– dfeuer
Jun 3 at 18:03
|
show 2 more comments
In Haskell, functors can almost always be derived, is there any case where a type is a functor and satisfies functor laws (such as fmap id == id
) but cannot be derived according to a simple set of rules?
And what about Foldable, Traversable, Semigroup and others? Is there any non-trivial cases available?
haskell functor category-theory
In Haskell, functors can almost always be derived, is there any case where a type is a functor and satisfies functor laws (such as fmap id == id
) but cannot be derived according to a simple set of rules?
And what about Foldable, Traversable, Semigroup and others? Is there any non-trivial cases available?
haskell functor category-theory
haskell functor category-theory
edited Jun 3 at 11:34
Micha Wiedenmann
10.9k1465107
10.9k1465107
asked Jun 3 at 6:40
Jiaming LuJiaming Lu
663417
663417
You mean something like tuple when you don't know should you apply function to first value or second?
– talex
Jun 3 at 6:43
@talex No, if you treat a tuple of (a, b) as a functor over b, then the result of fmap is (a, c), from which it's very clear which value shoud the function b -> c be applied to.
– Jiaming Lu
Jun 3 at 7:08
2
Functor in Haskell can always be derived. Bartosz Milewski talks about this in his book *Category Theory for Programmers*[1]. I need to look up the exact reference and details myself but it has to do with functors in Haskell forming some sort of an algebra. [1] bartoszmilewski.com/2014/10/28/…
– michid
Jun 3 at 7:20
5
Foldable, Traversable, and Semigroup all have symmetries which allow them to be lawfully instantiated in multiple distinct ways, which is one sense of nontriviality at least
– luqui
Jun 3 at 7:27
1
@luqui, if you use an Atkey-style indexed applicative to define a notion of traversable for type-aligned collections, then you should recover uniqueness.
– dfeuer
Jun 3 at 18:03
|
show 2 more comments
You mean something like tuple when you don't know should you apply function to first value or second?
– talex
Jun 3 at 6:43
@talex No, if you treat a tuple of (a, b) as a functor over b, then the result of fmap is (a, c), from which it's very clear which value shoud the function b -> c be applied to.
– Jiaming Lu
Jun 3 at 7:08
2
Functor in Haskell can always be derived. Bartosz Milewski talks about this in his book *Category Theory for Programmers*[1]. I need to look up the exact reference and details myself but it has to do with functors in Haskell forming some sort of an algebra. [1] bartoszmilewski.com/2014/10/28/…
– michid
Jun 3 at 7:20
5
Foldable, Traversable, and Semigroup all have symmetries which allow them to be lawfully instantiated in multiple distinct ways, which is one sense of nontriviality at least
– luqui
Jun 3 at 7:27
1
@luqui, if you use an Atkey-style indexed applicative to define a notion of traversable for type-aligned collections, then you should recover uniqueness.
– dfeuer
Jun 3 at 18:03
You mean something like tuple when you don't know should you apply function to first value or second?
– talex
Jun 3 at 6:43
You mean something like tuple when you don't know should you apply function to first value or second?
– talex
Jun 3 at 6:43
@talex No, if you treat a tuple of (a, b) as a functor over b, then the result of fmap is (a, c), from which it's very clear which value shoud the function b -> c be applied to.
– Jiaming Lu
Jun 3 at 7:08
@talex No, if you treat a tuple of (a, b) as a functor over b, then the result of fmap is (a, c), from which it's very clear which value shoud the function b -> c be applied to.
– Jiaming Lu
Jun 3 at 7:08
2
2
Functor in Haskell can always be derived. Bartosz Milewski talks about this in his book *Category Theory for Programmers*[1]. I need to look up the exact reference and details myself but it has to do with functors in Haskell forming some sort of an algebra. [1] bartoszmilewski.com/2014/10/28/…
– michid
Jun 3 at 7:20
Functor in Haskell can always be derived. Bartosz Milewski talks about this in his book *Category Theory for Programmers*[1]. I need to look up the exact reference and details myself but it has to do with functors in Haskell forming some sort of an algebra. [1] bartoszmilewski.com/2014/10/28/…
– michid
Jun 3 at 7:20
5
5
Foldable, Traversable, and Semigroup all have symmetries which allow them to be lawfully instantiated in multiple distinct ways, which is one sense of nontriviality at least
– luqui
Jun 3 at 7:27
Foldable, Traversable, and Semigroup all have symmetries which allow them to be lawfully instantiated in multiple distinct ways, which is one sense of nontriviality at least
– luqui
Jun 3 at 7:27
1
1
@luqui, if you use an Atkey-style indexed applicative to define a notion of traversable for type-aligned collections, then you should recover uniqueness.
– dfeuer
Jun 3 at 18:03
@luqui, if you use an Atkey-style indexed applicative to define a notion of traversable for type-aligned collections, then you should recover uniqueness.
– dfeuer
Jun 3 at 18:03
|
show 2 more comments
5 Answers
5
active
oldest
votes
Here's kind of a fun argument I just stumbled on. (It's late so I wonder if it will be sensical tomorrow)
We can construct the type of proofs of SK reducibility as a GADT:
infixl 9 :%:
data Term = S | K | Term :%: Term
-- small step, you can get from t to t' in one step
data Red1 t t' where
Red1S :: Red1 (S :%: x :%: y :%: z) (x :%: z :%: (y :%: z))
...
Now let's make a type which hides its functorhood at the end of a reduction chain.
data Red t a where
RedStep :: Red1 t t' -> Red t' a -> Red t a
RedK :: a -> Red K a
RedS :: (a -> Bool) -> Red S a
Now Red t
is a Functor
if t
normalizes to K
, but not if it normalizes to S
-- an undecidable problem. So perhaps you can still follow a "simple set of rules", but if you allow GADTs, the rules have to be powerful enough to compute anything.
(There is an alternative formulation which I find rather elegant, but maybe less demonstrative: if you drop the RedK
constructor, then Red t
is a Functor
if and only if the type system can express that the reduction of t
diverges -- and sometimes it diverges but you can't prove it, and my mind boggles about whether it's really a functor in that case or not)
Why might it not be a functor if reduction diverges? If you choose a deterministic reduction strategy inRed1
, definedata Blue t where BlueStep :: Red1 t t' -> Blue t' -> Blue t
. (For a non-deterministic strategy, things get kind of ugly, because you have to separate out all the overlapping rules). Now you can consumeRed t a
alongsideBlue t
. At each step, theRed1 t t'
in theBlue t
proves thatt
is notS
.
– dfeuer
Jun 3 at 18:48
@dfeuer, hm I'm not sure I'm following. Sure we can get thatt
is notS
at each step, but in order to prove functorhood we need that at every step
– luqui
Jun 3 at 22:55
1
I haven't quite gotten there, but this gist sets up the framework I sketched, and goes much of the way toward expressing a proof of divergence for a particular example.
– dfeuer
Jun 4 at 3:31
add a comment |
Explicitly empty parametric types can be made into functors automatically:
data T a deriving Functor
However, implicitly empty ones can not:
import Data.Void
data T a = K a (a -> Void)
deriving Functor -- fails
-
error:
• Can't make a derived instance of ‘Functor T’:
Constructor ‘K’ must not use the type variable in a function argument
• In the data declaration for ‘T’
-
However,
instance Functor T where
fmap f (K x y) = absurd (y x)
is arguably a legal instance.
One could argue that, exploiting bottoms, one can find a counterexample to the functor laws for the instance above. In such case, however, I wonder if all the "standard" functor instances are actually lawful, even in the presence of bottoms. (Maybe they are?)
3
Most of the usualFunctor
instances are valid even with bottoms, I believe. Here's a simple type GHC can't deriveFunctor
for:data Foo a = Foo (a -> Bool) !Void
. There's another way contravariance can get in the way:newtype Bar f a = Bar (f a -> Bool)
. GHC can't derive the perfectly valid instanceinstance Contravariant f => Functor (Bar f) where fmap f (Bar g) = Bar (g . contramap f)
. The main trouble is that addingContravariant
into the deriving mix leads to be multiple potential instances (with incompatible constraints) for things likenewtype Baz f g a = Baz (f (g a))
.
– dfeuer
Jun 3 at 18:17
add a comment |
There are no non-trivial functors in the sense of the question. All functors can be derived mechanically as sums (Either
) and products (Tuple
) of the Identity
and the Const
functor. See the section about Functorial Algebraic Data Types for how this construction works in detail.
On a higher level of abstraction this works because Haskell's type form a Cartesian Closed Category where terminal objects, all products and all exponentials exist.
add a comment |
It's a bit cheaty, but here we go. According to this then functor cannot be derived automatically when there's a constraint on the type, eg.
data A a where
A1 :: (Ord a) => a -> A a
deriving instance Functor A -- doesn't work
and indeed, if (say) we wrote a manual version, it wouldn't work either.
instance Functor A where
fmap f (A1 a) = A1 (f a) -- Can't deduce Ord for f a
However, because all the algorithm's doing is checking that no constraint exists, we can introduce a typeclass of which every type is a member.
class C c
instance C c
Now proceeding as above with C
instead of Ord
,
data B b where
B1 :: (C b) => b -> B b
deriving instance Functor B -- doesn't work
instance Functor B where
fmap f (B1 b) = B1 (f b) -- does work!
add a comment |
There is a standard type in base
called Compose
defined like this:
newtype Compose f g a = Compose getCompose :: f (g a)
The derived Functor
instance is implemented this way:
instance (Functor f, Functor g) => Functor (Compose f g) where
fmap f (Compose v) = Compose (fmap (fmap f) v)
But there is another perfectly lawful instance with different behavior:
instance (Contravariant f, Contravariant g) => Functor (Compose f g) where
fmap f (Compose v) = Compose (contramap (contramap f) v)
To me, the fact that two distinct instances are available for Compose
suggests to me that no set of rules can be automatically applied to cover all possible cases.
Where both sets of constraints hold (i.e., when bothf
andg
have phantom parameters), the instances have the same behavior. So I'd say this is about incompatible constraints rather than different behaviors.
– dfeuer
Jun 4 at 9:35
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f56422226%2fexample-of-non-trivial-functors%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
5 Answers
5
active
oldest
votes
5 Answers
5
active
oldest
votes
active
oldest
votes
active
oldest
votes
Here's kind of a fun argument I just stumbled on. (It's late so I wonder if it will be sensical tomorrow)
We can construct the type of proofs of SK reducibility as a GADT:
infixl 9 :%:
data Term = S | K | Term :%: Term
-- small step, you can get from t to t' in one step
data Red1 t t' where
Red1S :: Red1 (S :%: x :%: y :%: z) (x :%: z :%: (y :%: z))
...
Now let's make a type which hides its functorhood at the end of a reduction chain.
data Red t a where
RedStep :: Red1 t t' -> Red t' a -> Red t a
RedK :: a -> Red K a
RedS :: (a -> Bool) -> Red S a
Now Red t
is a Functor
if t
normalizes to K
, but not if it normalizes to S
-- an undecidable problem. So perhaps you can still follow a "simple set of rules", but if you allow GADTs, the rules have to be powerful enough to compute anything.
(There is an alternative formulation which I find rather elegant, but maybe less demonstrative: if you drop the RedK
constructor, then Red t
is a Functor
if and only if the type system can express that the reduction of t
diverges -- and sometimes it diverges but you can't prove it, and my mind boggles about whether it's really a functor in that case or not)
Why might it not be a functor if reduction diverges? If you choose a deterministic reduction strategy inRed1
, definedata Blue t where BlueStep :: Red1 t t' -> Blue t' -> Blue t
. (For a non-deterministic strategy, things get kind of ugly, because you have to separate out all the overlapping rules). Now you can consumeRed t a
alongsideBlue t
. At each step, theRed1 t t'
in theBlue t
proves thatt
is notS
.
– dfeuer
Jun 3 at 18:48
@dfeuer, hm I'm not sure I'm following. Sure we can get thatt
is notS
at each step, but in order to prove functorhood we need that at every step
– luqui
Jun 3 at 22:55
1
I haven't quite gotten there, but this gist sets up the framework I sketched, and goes much of the way toward expressing a proof of divergence for a particular example.
– dfeuer
Jun 4 at 3:31
add a comment |
Here's kind of a fun argument I just stumbled on. (It's late so I wonder if it will be sensical tomorrow)
We can construct the type of proofs of SK reducibility as a GADT:
infixl 9 :%:
data Term = S | K | Term :%: Term
-- small step, you can get from t to t' in one step
data Red1 t t' where
Red1S :: Red1 (S :%: x :%: y :%: z) (x :%: z :%: (y :%: z))
...
Now let's make a type which hides its functorhood at the end of a reduction chain.
data Red t a where
RedStep :: Red1 t t' -> Red t' a -> Red t a
RedK :: a -> Red K a
RedS :: (a -> Bool) -> Red S a
Now Red t
is a Functor
if t
normalizes to K
, but not if it normalizes to S
-- an undecidable problem. So perhaps you can still follow a "simple set of rules", but if you allow GADTs, the rules have to be powerful enough to compute anything.
(There is an alternative formulation which I find rather elegant, but maybe less demonstrative: if you drop the RedK
constructor, then Red t
is a Functor
if and only if the type system can express that the reduction of t
diverges -- and sometimes it diverges but you can't prove it, and my mind boggles about whether it's really a functor in that case or not)
Why might it not be a functor if reduction diverges? If you choose a deterministic reduction strategy inRed1
, definedata Blue t where BlueStep :: Red1 t t' -> Blue t' -> Blue t
. (For a non-deterministic strategy, things get kind of ugly, because you have to separate out all the overlapping rules). Now you can consumeRed t a
alongsideBlue t
. At each step, theRed1 t t'
in theBlue t
proves thatt
is notS
.
– dfeuer
Jun 3 at 18:48
@dfeuer, hm I'm not sure I'm following. Sure we can get thatt
is notS
at each step, but in order to prove functorhood we need that at every step
– luqui
Jun 3 at 22:55
1
I haven't quite gotten there, but this gist sets up the framework I sketched, and goes much of the way toward expressing a proof of divergence for a particular example.
– dfeuer
Jun 4 at 3:31
add a comment |
Here's kind of a fun argument I just stumbled on. (It's late so I wonder if it will be sensical tomorrow)
We can construct the type of proofs of SK reducibility as a GADT:
infixl 9 :%:
data Term = S | K | Term :%: Term
-- small step, you can get from t to t' in one step
data Red1 t t' where
Red1S :: Red1 (S :%: x :%: y :%: z) (x :%: z :%: (y :%: z))
...
Now let's make a type which hides its functorhood at the end of a reduction chain.
data Red t a where
RedStep :: Red1 t t' -> Red t' a -> Red t a
RedK :: a -> Red K a
RedS :: (a -> Bool) -> Red S a
Now Red t
is a Functor
if t
normalizes to K
, but not if it normalizes to S
-- an undecidable problem. So perhaps you can still follow a "simple set of rules", but if you allow GADTs, the rules have to be powerful enough to compute anything.
(There is an alternative formulation which I find rather elegant, but maybe less demonstrative: if you drop the RedK
constructor, then Red t
is a Functor
if and only if the type system can express that the reduction of t
diverges -- and sometimes it diverges but you can't prove it, and my mind boggles about whether it's really a functor in that case or not)
Here's kind of a fun argument I just stumbled on. (It's late so I wonder if it will be sensical tomorrow)
We can construct the type of proofs of SK reducibility as a GADT:
infixl 9 :%:
data Term = S | K | Term :%: Term
-- small step, you can get from t to t' in one step
data Red1 t t' where
Red1S :: Red1 (S :%: x :%: y :%: z) (x :%: z :%: (y :%: z))
...
Now let's make a type which hides its functorhood at the end of a reduction chain.
data Red t a where
RedStep :: Red1 t t' -> Red t' a -> Red t a
RedK :: a -> Red K a
RedS :: (a -> Bool) -> Red S a
Now Red t
is a Functor
if t
normalizes to K
, but not if it normalizes to S
-- an undecidable problem. So perhaps you can still follow a "simple set of rules", but if you allow GADTs, the rules have to be powerful enough to compute anything.
(There is an alternative formulation which I find rather elegant, but maybe less demonstrative: if you drop the RedK
constructor, then Red t
is a Functor
if and only if the type system can express that the reduction of t
diverges -- and sometimes it diverges but you can't prove it, and my mind boggles about whether it's really a functor in that case or not)
edited Jun 4 at 4:01
dfeuer
34.4k350135
34.4k350135
answered Jun 3 at 7:54
luquiluqui
49.3k6116171
49.3k6116171
Why might it not be a functor if reduction diverges? If you choose a deterministic reduction strategy inRed1
, definedata Blue t where BlueStep :: Red1 t t' -> Blue t' -> Blue t
. (For a non-deterministic strategy, things get kind of ugly, because you have to separate out all the overlapping rules). Now you can consumeRed t a
alongsideBlue t
. At each step, theRed1 t t'
in theBlue t
proves thatt
is notS
.
– dfeuer
Jun 3 at 18:48
@dfeuer, hm I'm not sure I'm following. Sure we can get thatt
is notS
at each step, but in order to prove functorhood we need that at every step
– luqui
Jun 3 at 22:55
1
I haven't quite gotten there, but this gist sets up the framework I sketched, and goes much of the way toward expressing a proof of divergence for a particular example.
– dfeuer
Jun 4 at 3:31
add a comment |
Why might it not be a functor if reduction diverges? If you choose a deterministic reduction strategy inRed1
, definedata Blue t where BlueStep :: Red1 t t' -> Blue t' -> Blue t
. (For a non-deterministic strategy, things get kind of ugly, because you have to separate out all the overlapping rules). Now you can consumeRed t a
alongsideBlue t
. At each step, theRed1 t t'
in theBlue t
proves thatt
is notS
.
– dfeuer
Jun 3 at 18:48
@dfeuer, hm I'm not sure I'm following. Sure we can get thatt
is notS
at each step, but in order to prove functorhood we need that at every step
– luqui
Jun 3 at 22:55
1
I haven't quite gotten there, but this gist sets up the framework I sketched, and goes much of the way toward expressing a proof of divergence for a particular example.
– dfeuer
Jun 4 at 3:31
Why might it not be a functor if reduction diverges? If you choose a deterministic reduction strategy in
Red1
, define data Blue t where BlueStep :: Red1 t t' -> Blue t' -> Blue t
. (For a non-deterministic strategy, things get kind of ugly, because you have to separate out all the overlapping rules). Now you can consume Red t a
alongside Blue t
. At each step, the Red1 t t'
in the Blue t
proves that t
is not S
.– dfeuer
Jun 3 at 18:48
Why might it not be a functor if reduction diverges? If you choose a deterministic reduction strategy in
Red1
, define data Blue t where BlueStep :: Red1 t t' -> Blue t' -> Blue t
. (For a non-deterministic strategy, things get kind of ugly, because you have to separate out all the overlapping rules). Now you can consume Red t a
alongside Blue t
. At each step, the Red1 t t'
in the Blue t
proves that t
is not S
.– dfeuer
Jun 3 at 18:48
@dfeuer, hm I'm not sure I'm following. Sure we can get that
t
is not S
at each step, but in order to prove functorhood we need that at every step– luqui
Jun 3 at 22:55
@dfeuer, hm I'm not sure I'm following. Sure we can get that
t
is not S
at each step, but in order to prove functorhood we need that at every step– luqui
Jun 3 at 22:55
1
1
I haven't quite gotten there, but this gist sets up the framework I sketched, and goes much of the way toward expressing a proof of divergence for a particular example.
– dfeuer
Jun 4 at 3:31
I haven't quite gotten there, but this gist sets up the framework I sketched, and goes much of the way toward expressing a proof of divergence for a particular example.
– dfeuer
Jun 4 at 3:31
add a comment |
Explicitly empty parametric types can be made into functors automatically:
data T a deriving Functor
However, implicitly empty ones can not:
import Data.Void
data T a = K a (a -> Void)
deriving Functor -- fails
-
error:
• Can't make a derived instance of ‘Functor T’:
Constructor ‘K’ must not use the type variable in a function argument
• In the data declaration for ‘T’
-
However,
instance Functor T where
fmap f (K x y) = absurd (y x)
is arguably a legal instance.
One could argue that, exploiting bottoms, one can find a counterexample to the functor laws for the instance above. In such case, however, I wonder if all the "standard" functor instances are actually lawful, even in the presence of bottoms. (Maybe they are?)
3
Most of the usualFunctor
instances are valid even with bottoms, I believe. Here's a simple type GHC can't deriveFunctor
for:data Foo a = Foo (a -> Bool) !Void
. There's another way contravariance can get in the way:newtype Bar f a = Bar (f a -> Bool)
. GHC can't derive the perfectly valid instanceinstance Contravariant f => Functor (Bar f) where fmap f (Bar g) = Bar (g . contramap f)
. The main trouble is that addingContravariant
into the deriving mix leads to be multiple potential instances (with incompatible constraints) for things likenewtype Baz f g a = Baz (f (g a))
.
– dfeuer
Jun 3 at 18:17
add a comment |
Explicitly empty parametric types can be made into functors automatically:
data T a deriving Functor
However, implicitly empty ones can not:
import Data.Void
data T a = K a (a -> Void)
deriving Functor -- fails
-
error:
• Can't make a derived instance of ‘Functor T’:
Constructor ‘K’ must not use the type variable in a function argument
• In the data declaration for ‘T’
-
However,
instance Functor T where
fmap f (K x y) = absurd (y x)
is arguably a legal instance.
One could argue that, exploiting bottoms, one can find a counterexample to the functor laws for the instance above. In such case, however, I wonder if all the "standard" functor instances are actually lawful, even in the presence of bottoms. (Maybe they are?)
3
Most of the usualFunctor
instances are valid even with bottoms, I believe. Here's a simple type GHC can't deriveFunctor
for:data Foo a = Foo (a -> Bool) !Void
. There's another way contravariance can get in the way:newtype Bar f a = Bar (f a -> Bool)
. GHC can't derive the perfectly valid instanceinstance Contravariant f => Functor (Bar f) where fmap f (Bar g) = Bar (g . contramap f)
. The main trouble is that addingContravariant
into the deriving mix leads to be multiple potential instances (with incompatible constraints) for things likenewtype Baz f g a = Baz (f (g a))
.
– dfeuer
Jun 3 at 18:17
add a comment |
Explicitly empty parametric types can be made into functors automatically:
data T a deriving Functor
However, implicitly empty ones can not:
import Data.Void
data T a = K a (a -> Void)
deriving Functor -- fails
-
error:
• Can't make a derived instance of ‘Functor T’:
Constructor ‘K’ must not use the type variable in a function argument
• In the data declaration for ‘T’
-
However,
instance Functor T where
fmap f (K x y) = absurd (y x)
is arguably a legal instance.
One could argue that, exploiting bottoms, one can find a counterexample to the functor laws for the instance above. In such case, however, I wonder if all the "standard" functor instances are actually lawful, even in the presence of bottoms. (Maybe they are?)
Explicitly empty parametric types can be made into functors automatically:
data T a deriving Functor
However, implicitly empty ones can not:
import Data.Void
data T a = K a (a -> Void)
deriving Functor -- fails
-
error:
• Can't make a derived instance of ‘Functor T’:
Constructor ‘K’ must not use the type variable in a function argument
• In the data declaration for ‘T’
-
However,
instance Functor T where
fmap f (K x y) = absurd (y x)
is arguably a legal instance.
One could argue that, exploiting bottoms, one can find a counterexample to the functor laws for the instance above. In such case, however, I wonder if all the "standard" functor instances are actually lawful, even in the presence of bottoms. (Maybe they are?)
edited Jun 3 at 10:22
answered Jun 3 at 10:05
chichi
79.4k288151
79.4k288151
3
Most of the usualFunctor
instances are valid even with bottoms, I believe. Here's a simple type GHC can't deriveFunctor
for:data Foo a = Foo (a -> Bool) !Void
. There's another way contravariance can get in the way:newtype Bar f a = Bar (f a -> Bool)
. GHC can't derive the perfectly valid instanceinstance Contravariant f => Functor (Bar f) where fmap f (Bar g) = Bar (g . contramap f)
. The main trouble is that addingContravariant
into the deriving mix leads to be multiple potential instances (with incompatible constraints) for things likenewtype Baz f g a = Baz (f (g a))
.
– dfeuer
Jun 3 at 18:17
add a comment |
3
Most of the usualFunctor
instances are valid even with bottoms, I believe. Here's a simple type GHC can't deriveFunctor
for:data Foo a = Foo (a -> Bool) !Void
. There's another way contravariance can get in the way:newtype Bar f a = Bar (f a -> Bool)
. GHC can't derive the perfectly valid instanceinstance Contravariant f => Functor (Bar f) where fmap f (Bar g) = Bar (g . contramap f)
. The main trouble is that addingContravariant
into the deriving mix leads to be multiple potential instances (with incompatible constraints) for things likenewtype Baz f g a = Baz (f (g a))
.
– dfeuer
Jun 3 at 18:17
3
3
Most of the usual
Functor
instances are valid even with bottoms, I believe. Here's a simple type GHC can't derive Functor
for: data Foo a = Foo (a -> Bool) !Void
. There's another way contravariance can get in the way: newtype Bar f a = Bar (f a -> Bool)
. GHC can't derive the perfectly valid instance instance Contravariant f => Functor (Bar f) where fmap f (Bar g) = Bar (g . contramap f)
. The main trouble is that adding Contravariant
into the deriving mix leads to be multiple potential instances (with incompatible constraints) for things like newtype Baz f g a = Baz (f (g a))
.– dfeuer
Jun 3 at 18:17
Most of the usual
Functor
instances are valid even with bottoms, I believe. Here's a simple type GHC can't derive Functor
for: data Foo a = Foo (a -> Bool) !Void
. There's another way contravariance can get in the way: newtype Bar f a = Bar (f a -> Bool)
. GHC can't derive the perfectly valid instance instance Contravariant f => Functor (Bar f) where fmap f (Bar g) = Bar (g . contramap f)
. The main trouble is that adding Contravariant
into the deriving mix leads to be multiple potential instances (with incompatible constraints) for things like newtype Baz f g a = Baz (f (g a))
.– dfeuer
Jun 3 at 18:17
add a comment |
There are no non-trivial functors in the sense of the question. All functors can be derived mechanically as sums (Either
) and products (Tuple
) of the Identity
and the Const
functor. See the section about Functorial Algebraic Data Types for how this construction works in detail.
On a higher level of abstraction this works because Haskell's type form a Cartesian Closed Category where terminal objects, all products and all exponentials exist.
add a comment |
There are no non-trivial functors in the sense of the question. All functors can be derived mechanically as sums (Either
) and products (Tuple
) of the Identity
and the Const
functor. See the section about Functorial Algebraic Data Types for how this construction works in detail.
On a higher level of abstraction this works because Haskell's type form a Cartesian Closed Category where terminal objects, all products and all exponentials exist.
add a comment |
There are no non-trivial functors in the sense of the question. All functors can be derived mechanically as sums (Either
) and products (Tuple
) of the Identity
and the Const
functor. See the section about Functorial Algebraic Data Types for how this construction works in detail.
On a higher level of abstraction this works because Haskell's type form a Cartesian Closed Category where terminal objects, all products and all exponentials exist.
There are no non-trivial functors in the sense of the question. All functors can be derived mechanically as sums (Either
) and products (Tuple
) of the Identity
and the Const
functor. See the section about Functorial Algebraic Data Types for how this construction works in detail.
On a higher level of abstraction this works because Haskell's type form a Cartesian Closed Category where terminal objects, all products and all exponentials exist.
edited Jun 4 at 9:37
dfeuer
34.4k350135
34.4k350135
answered Jun 3 at 7:49
michidmichid
6,01322039
6,01322039
add a comment |
add a comment |
It's a bit cheaty, but here we go. According to this then functor cannot be derived automatically when there's a constraint on the type, eg.
data A a where
A1 :: (Ord a) => a -> A a
deriving instance Functor A -- doesn't work
and indeed, if (say) we wrote a manual version, it wouldn't work either.
instance Functor A where
fmap f (A1 a) = A1 (f a) -- Can't deduce Ord for f a
However, because all the algorithm's doing is checking that no constraint exists, we can introduce a typeclass of which every type is a member.
class C c
instance C c
Now proceeding as above with C
instead of Ord
,
data B b where
B1 :: (C b) => b -> B b
deriving instance Functor B -- doesn't work
instance Functor B where
fmap f (B1 b) = B1 (f b) -- does work!
add a comment |
It's a bit cheaty, but here we go. According to this then functor cannot be derived automatically when there's a constraint on the type, eg.
data A a where
A1 :: (Ord a) => a -> A a
deriving instance Functor A -- doesn't work
and indeed, if (say) we wrote a manual version, it wouldn't work either.
instance Functor A where
fmap f (A1 a) = A1 (f a) -- Can't deduce Ord for f a
However, because all the algorithm's doing is checking that no constraint exists, we can introduce a typeclass of which every type is a member.
class C c
instance C c
Now proceeding as above with C
instead of Ord
,
data B b where
B1 :: (C b) => b -> B b
deriving instance Functor B -- doesn't work
instance Functor B where
fmap f (B1 b) = B1 (f b) -- does work!
add a comment |
It's a bit cheaty, but here we go. According to this then functor cannot be derived automatically when there's a constraint on the type, eg.
data A a where
A1 :: (Ord a) => a -> A a
deriving instance Functor A -- doesn't work
and indeed, if (say) we wrote a manual version, it wouldn't work either.
instance Functor A where
fmap f (A1 a) = A1 (f a) -- Can't deduce Ord for f a
However, because all the algorithm's doing is checking that no constraint exists, we can introduce a typeclass of which every type is a member.
class C c
instance C c
Now proceeding as above with C
instead of Ord
,
data B b where
B1 :: (C b) => b -> B b
deriving instance Functor B -- doesn't work
instance Functor B where
fmap f (B1 b) = B1 (f b) -- does work!
It's a bit cheaty, but here we go. According to this then functor cannot be derived automatically when there's a constraint on the type, eg.
data A a where
A1 :: (Ord a) => a -> A a
deriving instance Functor A -- doesn't work
and indeed, if (say) we wrote a manual version, it wouldn't work either.
instance Functor A where
fmap f (A1 a) = A1 (f a) -- Can't deduce Ord for f a
However, because all the algorithm's doing is checking that no constraint exists, we can introduce a typeclass of which every type is a member.
class C c
instance C c
Now proceeding as above with C
instead of Ord
,
data B b where
B1 :: (C b) => b -> B b
deriving instance Functor B -- doesn't work
instance Functor B where
fmap f (B1 b) = B1 (f b) -- does work!
answered Jun 3 at 7:59
moonGoosemoonGoose
1,163212
1,163212
add a comment |
add a comment |
There is a standard type in base
called Compose
defined like this:
newtype Compose f g a = Compose getCompose :: f (g a)
The derived Functor
instance is implemented this way:
instance (Functor f, Functor g) => Functor (Compose f g) where
fmap f (Compose v) = Compose (fmap (fmap f) v)
But there is another perfectly lawful instance with different behavior:
instance (Contravariant f, Contravariant g) => Functor (Compose f g) where
fmap f (Compose v) = Compose (contramap (contramap f) v)
To me, the fact that two distinct instances are available for Compose
suggests to me that no set of rules can be automatically applied to cover all possible cases.
Where both sets of constraints hold (i.e., when bothf
andg
have phantom parameters), the instances have the same behavior. So I'd say this is about incompatible constraints rather than different behaviors.
– dfeuer
Jun 4 at 9:35
add a comment |
There is a standard type in base
called Compose
defined like this:
newtype Compose f g a = Compose getCompose :: f (g a)
The derived Functor
instance is implemented this way:
instance (Functor f, Functor g) => Functor (Compose f g) where
fmap f (Compose v) = Compose (fmap (fmap f) v)
But there is another perfectly lawful instance with different behavior:
instance (Contravariant f, Contravariant g) => Functor (Compose f g) where
fmap f (Compose v) = Compose (contramap (contramap f) v)
To me, the fact that two distinct instances are available for Compose
suggests to me that no set of rules can be automatically applied to cover all possible cases.
Where both sets of constraints hold (i.e., when bothf
andg
have phantom parameters), the instances have the same behavior. So I'd say this is about incompatible constraints rather than different behaviors.
– dfeuer
Jun 4 at 9:35
add a comment |
There is a standard type in base
called Compose
defined like this:
newtype Compose f g a = Compose getCompose :: f (g a)
The derived Functor
instance is implemented this way:
instance (Functor f, Functor g) => Functor (Compose f g) where
fmap f (Compose v) = Compose (fmap (fmap f) v)
But there is another perfectly lawful instance with different behavior:
instance (Contravariant f, Contravariant g) => Functor (Compose f g) where
fmap f (Compose v) = Compose (contramap (contramap f) v)
To me, the fact that two distinct instances are available for Compose
suggests to me that no set of rules can be automatically applied to cover all possible cases.
There is a standard type in base
called Compose
defined like this:
newtype Compose f g a = Compose getCompose :: f (g a)
The derived Functor
instance is implemented this way:
instance (Functor f, Functor g) => Functor (Compose f g) where
fmap f (Compose v) = Compose (fmap (fmap f) v)
But there is another perfectly lawful instance with different behavior:
instance (Contravariant f, Contravariant g) => Functor (Compose f g) where
fmap f (Compose v) = Compose (contramap (contramap f) v)
To me, the fact that two distinct instances are available for Compose
suggests to me that no set of rules can be automatically applied to cover all possible cases.
answered Jun 4 at 3:14
Daniel WagnerDaniel Wagner
107k7164294
107k7164294
Where both sets of constraints hold (i.e., when bothf
andg
have phantom parameters), the instances have the same behavior. So I'd say this is about incompatible constraints rather than different behaviors.
– dfeuer
Jun 4 at 9:35
add a comment |
Where both sets of constraints hold (i.e., when bothf
andg
have phantom parameters), the instances have the same behavior. So I'd say this is about incompatible constraints rather than different behaviors.
– dfeuer
Jun 4 at 9:35
Where both sets of constraints hold (i.e., when both
f
and g
have phantom parameters), the instances have the same behavior. So I'd say this is about incompatible constraints rather than different behaviors.– dfeuer
Jun 4 at 9:35
Where both sets of constraints hold (i.e., when both
f
and g
have phantom parameters), the instances have the same behavior. So I'd say this is about incompatible constraints rather than different behaviors.– dfeuer
Jun 4 at 9:35
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f56422226%2fexample-of-non-trivial-functors%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
You mean something like tuple when you don't know should you apply function to first value or second?
– talex
Jun 3 at 6:43
@talex No, if you treat a tuple of (a, b) as a functor over b, then the result of fmap is (a, c), from which it's very clear which value shoud the function b -> c be applied to.
– Jiaming Lu
Jun 3 at 7:08
2
Functor in Haskell can always be derived. Bartosz Milewski talks about this in his book *Category Theory for Programmers*[1]. I need to look up the exact reference and details myself but it has to do with functors in Haskell forming some sort of an algebra. [1] bartoszmilewski.com/2014/10/28/…
– michid
Jun 3 at 7:20
5
Foldable, Traversable, and Semigroup all have symmetries which allow them to be lawfully instantiated in multiple distinct ways, which is one sense of nontriviality at least
– luqui
Jun 3 at 7:27
1
@luqui, if you use an Atkey-style indexed applicative to define a notion of traversable for type-aligned collections, then you should recover uniqueness.
– dfeuer
Jun 3 at 18:03