How to “extend” classes in HaskellGetting started with HaskellWhat is Haskell used for in the real world?Large-scale design in Haskell?Folding over a polymorphic list in HaskellGHC code generation for type class function callsHaskell: YesNo type class. Why Integer?Why not a more general function than mappend in Haskell?How is the instance of Show for String written?Haskell GADT typesafe Evaluator: Constant term for more than one type.Constraint on associated type in Haskell
How to duplicate all folders into same directory?
Does the House Resolution about the Impeachment Inquiry change anything?
Totally Blind Chess
Is the Night's Watch voting system based on history?
What are valid bugs
Dodging a Deathbeam travelling at speed of light
What is the hidden passcode?
If ancient soldiers could firebend, would battle lines cease to exist?
What is David Chalmers' Naturalistic dualism?
Beam slope indicating accelerando or rallentando
Can a sauce with dairy be jarred?
How does an ideal op amp amplify a voltage input when the voltage difference is 0?
Languages which changed their writing direction
Lvl20 Samurai+true strike=9 attacks all with advantage?
Can abstractions and good code practice in embedded C++ eliminate the need for the debugger?
How likely are you to be injured by falling shot from a game shoot?
Implement batch option --yes in bash script
Help with formulating an implication
How did Beit Shammai and Beit Hillel arrive at the conclusion that it would have been preferable had man not been created?
I have been accused of copying two lab report’s from the previous year even though I had done everything by myself
What does it mean by commercial support available in Open source platform?
Proving there exist three different vectors that sum to zero
Why does this Ultramarine have red armour?
How does an all-female medieval country maintain itself?
How to “extend” classes in Haskell
Getting started with HaskellWhat is Haskell used for in the real world?Large-scale design in Haskell?Folding over a polymorphic list in HaskellGHC code generation for type class function callsHaskell: YesNo type class. Why Integer?Why not a more general function than mappend in Haskell?How is the instance of Show for String written?Haskell GADT typesafe Evaluator: Constant term for more than one type.Constraint on associated type in Haskell
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty
margin-bottom:0;
I want to create two typeclasses, A
and B
, where A
is a superclass of B
. The functions defined in B
are sufficient to implement those in A
. Then, if I have a function with the constraint fun :: (A thing) => ...
an instance of B
for, say, Int
, I'd like to be able to pass an Int
to fun
without creating a duplicate instance A
for Int
.
For example, let's say I have a type class which can check if value is "even". Then, I have another type class which can check if a value is divisible by some number. The second type class is powerful enough to implement the functions in the first, and any function which only requires "even-checking" capabilities should be able to accept an argument which has "divisible-by" abilities.
Here's what I think it would look like:
class IsEven a where
isEven :: a -> Bool
class (IsEven a) => DivisibleBy a where
divisibleBy :: a -> Int -> Bool
isEven :: a -> Bool
isEven a = divisibleBy a 2
printIsEven :: (IsEven a) => a -> IO ()
printIsEven a = putStrLn (show (IsEven.isEven a))
instance IsEven Int -- I need to do this or I cannot create a DivisibleBy instance
instance DivisibleBy Int where
divisibleBy a i = a `mod` i == 0
myint :: Int
myint = 2
main :: IO ()
main = printIsEven myint
However, at compile time this produces the warning:
[2 of 2] Compiling Main ( Foo.hs, Foo.o )
Foo.hs:11:10: warning: [-Wmissing-methods]
• No explicit implementation for
‘IsEven.isEven’
• In the instance declaration for ‘IsEven Int’
|
11 | instance IsEven Int
| ^^^^^^^^^^
Linking Foo ...
and at runtime, the program fails:
Foo: Foo.hs:11:10-19: No instance nor default method for class operation isEven
How can I achieve this subtyping effect without duplicating logic into an instance IsEven
?
haskell typeclass
add a comment
|
I want to create two typeclasses, A
and B
, where A
is a superclass of B
. The functions defined in B
are sufficient to implement those in A
. Then, if I have a function with the constraint fun :: (A thing) => ...
an instance of B
for, say, Int
, I'd like to be able to pass an Int
to fun
without creating a duplicate instance A
for Int
.
For example, let's say I have a type class which can check if value is "even". Then, I have another type class which can check if a value is divisible by some number. The second type class is powerful enough to implement the functions in the first, and any function which only requires "even-checking" capabilities should be able to accept an argument which has "divisible-by" abilities.
Here's what I think it would look like:
class IsEven a where
isEven :: a -> Bool
class (IsEven a) => DivisibleBy a where
divisibleBy :: a -> Int -> Bool
isEven :: a -> Bool
isEven a = divisibleBy a 2
printIsEven :: (IsEven a) => a -> IO ()
printIsEven a = putStrLn (show (IsEven.isEven a))
instance IsEven Int -- I need to do this or I cannot create a DivisibleBy instance
instance DivisibleBy Int where
divisibleBy a i = a `mod` i == 0
myint :: Int
myint = 2
main :: IO ()
main = printIsEven myint
However, at compile time this produces the warning:
[2 of 2] Compiling Main ( Foo.hs, Foo.o )
Foo.hs:11:10: warning: [-Wmissing-methods]
• No explicit implementation for
‘IsEven.isEven’
• In the instance declaration for ‘IsEven Int’
|
11 | instance IsEven Int
| ^^^^^^^^^^
Linking Foo ...
and at runtime, the program fails:
Foo: Foo.hs:11:10-19: No instance nor default method for class operation isEven
How can I achieve this subtyping effect without duplicating logic into an instance IsEven
?
haskell typeclass
3
There is no reason forDivisibleBy
to be a subclass ofIsEven
. The opposite relation is reasonable, as is making the two classes independent. Just because usingdivisibleBy
is one way to defineisEven
doesn't mean anIsEven
instance should be required.
– chepner
Jul 6 at 14:34
add a comment
|
I want to create two typeclasses, A
and B
, where A
is a superclass of B
. The functions defined in B
are sufficient to implement those in A
. Then, if I have a function with the constraint fun :: (A thing) => ...
an instance of B
for, say, Int
, I'd like to be able to pass an Int
to fun
without creating a duplicate instance A
for Int
.
For example, let's say I have a type class which can check if value is "even". Then, I have another type class which can check if a value is divisible by some number. The second type class is powerful enough to implement the functions in the first, and any function which only requires "even-checking" capabilities should be able to accept an argument which has "divisible-by" abilities.
Here's what I think it would look like:
class IsEven a where
isEven :: a -> Bool
class (IsEven a) => DivisibleBy a where
divisibleBy :: a -> Int -> Bool
isEven :: a -> Bool
isEven a = divisibleBy a 2
printIsEven :: (IsEven a) => a -> IO ()
printIsEven a = putStrLn (show (IsEven.isEven a))
instance IsEven Int -- I need to do this or I cannot create a DivisibleBy instance
instance DivisibleBy Int where
divisibleBy a i = a `mod` i == 0
myint :: Int
myint = 2
main :: IO ()
main = printIsEven myint
However, at compile time this produces the warning:
[2 of 2] Compiling Main ( Foo.hs, Foo.o )
Foo.hs:11:10: warning: [-Wmissing-methods]
• No explicit implementation for
‘IsEven.isEven’
• In the instance declaration for ‘IsEven Int’
|
11 | instance IsEven Int
| ^^^^^^^^^^
Linking Foo ...
and at runtime, the program fails:
Foo: Foo.hs:11:10-19: No instance nor default method for class operation isEven
How can I achieve this subtyping effect without duplicating logic into an instance IsEven
?
haskell typeclass
I want to create two typeclasses, A
and B
, where A
is a superclass of B
. The functions defined in B
are sufficient to implement those in A
. Then, if I have a function with the constraint fun :: (A thing) => ...
an instance of B
for, say, Int
, I'd like to be able to pass an Int
to fun
without creating a duplicate instance A
for Int
.
For example, let's say I have a type class which can check if value is "even". Then, I have another type class which can check if a value is divisible by some number. The second type class is powerful enough to implement the functions in the first, and any function which only requires "even-checking" capabilities should be able to accept an argument which has "divisible-by" abilities.
Here's what I think it would look like:
class IsEven a where
isEven :: a -> Bool
class (IsEven a) => DivisibleBy a where
divisibleBy :: a -> Int -> Bool
isEven :: a -> Bool
isEven a = divisibleBy a 2
printIsEven :: (IsEven a) => a -> IO ()
printIsEven a = putStrLn (show (IsEven.isEven a))
instance IsEven Int -- I need to do this or I cannot create a DivisibleBy instance
instance DivisibleBy Int where
divisibleBy a i = a `mod` i == 0
myint :: Int
myint = 2
main :: IO ()
main = printIsEven myint
However, at compile time this produces the warning:
[2 of 2] Compiling Main ( Foo.hs, Foo.o )
Foo.hs:11:10: warning: [-Wmissing-methods]
• No explicit implementation for
‘IsEven.isEven’
• In the instance declaration for ‘IsEven Int’
|
11 | instance IsEven Int
| ^^^^^^^^^^
Linking Foo ...
and at runtime, the program fails:
Foo: Foo.hs:11:10-19: No instance nor default method for class operation isEven
How can I achieve this subtyping effect without duplicating logic into an instance IsEven
?
haskell typeclass
haskell typeclass
edited Jul 6 at 20:28
Peter Mortensen
14.6k19 gold badges89 silver badges118 bronze badges
14.6k19 gold badges89 silver badges118 bronze badges
asked Jul 6 at 4:52
David AbrahamsDavid Abrahams
765 bronze badges
765 bronze badges
3
There is no reason forDivisibleBy
to be a subclass ofIsEven
. The opposite relation is reasonable, as is making the two classes independent. Just because usingdivisibleBy
is one way to defineisEven
doesn't mean anIsEven
instance should be required.
– chepner
Jul 6 at 14:34
add a comment
|
3
There is no reason forDivisibleBy
to be a subclass ofIsEven
. The opposite relation is reasonable, as is making the two classes independent. Just because usingdivisibleBy
is one way to defineisEven
doesn't mean anIsEven
instance should be required.
– chepner
Jul 6 at 14:34
3
3
There is no reason for
DivisibleBy
to be a subclass of IsEven
. The opposite relation is reasonable, as is making the two classes independent. Just because using divisibleBy
is one way to define isEven
doesn't mean an IsEven
instance should be required.– chepner
Jul 6 at 14:34
There is no reason for
DivisibleBy
to be a subclass of IsEven
. The opposite relation is reasonable, as is making the two classes independent. Just because using divisibleBy
is one way to define isEven
doesn't mean an IsEven
instance should be required.– chepner
Jul 6 at 14:34
add a comment
|
3 Answers
3
active
oldest
votes
As far as I know, the closest you can get in standard Haskell is
instance IsEven Int where
isEven n = n `divisibleBy` 2
instance DivisibleBy Int where
divisibleBy a i = a `mod` i == 0
You don't have to duplicate the logic (indeed, you can implement isEven
in terms of divisibleBy
), but you still need to provide an explicit definition.
You would have to repeat this pattern for every type you want to make an instance of DivisibleBy
.
Using the DefaultSignatures
language extension you can also do the following:
-# LANGUAGE DefaultSignatures #-
class IsEven a where
isEven :: a -> Bool
default isEven :: (DivisibleBy a) => a -> Bool
isEven n = n `divisibleBy` 2
class (IsEven a) => DivisibleBy a where
divisibleBy :: a -> Int -> Bool
instance IsEven Int
instance DivisibleBy Int where
divisibleBy a i = a `mod` i == 0
This moves the default implementation to the class itself. Now you can indeed just say instance IsEven Int
without providing an instance body. The disadvantage is that now IsEven
has to know about DivisibleBy
, and you can only provide one default
implementation.
It might be worth it to explicitly note that theliftM
idiom (in this case, definingisEvenDivisible :: DivisibleBy a => a -> Bool
for the sake of writing boilerplateIsEven
instances) might make the Haskell 98 solution slightly more comfortable. (Ultimately, I guess, it is a matter of how many extra names is one willing to tolerate, versus how often one can stand rewriting the full boilerplate implementation.)
– duplode
Jul 6 at 11:02
add a comment
|
You can't redefine a method in a new class and have it affect the one in the old class. If you want methods to work like this, the parent class has to reference the child class.
You need the DefaultSignatures
extension to make this work. Turn it on and then change your classes to this:
class IsEven a where
isEven :: a -> Bool
default isEven :: DivisibleBy a => a -> Bool
isEven a = divisibleBy a 2
class IsEven a => DivisibleBy a where
divisibleBy :: a -> Int -> Bool
hm, but what if then there is some other type class which also has enough functionality to implementisEven
? It seems like doing it this way requires the superclass to "know about" its subclass. In Scala, for example, you can subclass any typeclass and provide implementations for the superclasses methods. Eg,Applicative
can implementFunctor
'smap
usingap
: github.com/typelevel/cats/blob/master/core/src/main/scala/cats/… Is there a way to achieve something similar in Haskell? In Scala, if there is anApplicative
in scope, you get aFunctor
for free.
– David Abrahams
Jul 6 at 5:09
@DavidAbrahams I don't believe that Haskell currently has any equivalent to that functionality.
– Joseph Sible
Jul 6 at 5:11
add a comment
|
With GHC 8.6 and above, this can also be achieved through DerivingVia
:
-# LANGUAGE DerivingVia #-
-# LANGUAGE GeneralisedNewtypeDeriving #-
-# LANGUAGE StandaloneDeriving #-
-- Class definitions:
class IsEven a where
isEven :: a -> Bool
-- Note that we don't need to have IsEven as a superclass.
class DivisibleBy a where
divisibleBy :: a -> Int -> Bool
-- Boilerplate that only needs to be written once:
-- Boilerplate DivisibleBy instance generated with GeneralisedNewtypeDeriving.
newtype WrappedDivisibleBy a = WrapDivisibleBy unwrapDivisibleBy :: a
deriving DivisibleBy
instance DivisibleBy a => IsEven (WrappedDivisibleBy a) where
isEven n = n `divisibleBy` 2
-- Instance example:
instance DivisibleBy Int where
divisibleBy a i = a `mod` i == 0
-- Boilerplate IsEven instance generated with DerivingVia
-- (and StandaloneDeriving, as we aren't defining Int here).
deriving via (WrappedDivisibleBy Int) instance IsEven Int
DerivingVia
is not always an option (in the case of classes like Traversable
, which have an extra type constructor wrapping things in the type signature, it clashes with the role system); when it works, though, it is very neat.
Ah this is neat! I actually don't think we needDivisibleBy
to extendisEven
in this case, correct? (It compiles without it)
– David Abrahams
Jul 6 at 21:45
1
@DavidAbrahams We don't, indeed. I have updated the answer to reflect that. (While here I'm primarily dealing with the typeclass mechanics, chepner does have a point about this specific example, so dropping the relationship looks like a net gain as far as this answer is concerned.)
– duplode
Jul 6 at 21:53
1
I always wantedunsafe via
to bypass the role system, it would useunsafeCoerce
instead ofcoerce
– Iceland_jack
Jul 9 at 14:29
@Iceland_jack Tangential question: will theQuantifiedConstraints
approach discussed in this ticket allow derivingTraversable
andDistributive
? Though I might well be reading the situation wrongly, it looks like that would demand coercing under an arbitrary functor that the instance implementer has no control over.
– duplode
Jul 9 at 16:08
1
Not as smoothly asjoin
, requires big changes but I think it is the best path forward, theforall f a b. Applicative f =>
quantification appears in the type oftraverse @t @f @a..
, we can only work witht
. Either strengthenApplicative f
withRepresentational1
or decree allFunctor
s be representational in their first arg by makingclass Representational1 f => Functor f
a superclass. Ryan explains better:QuantifiedConstraints
and the trouble withTraversable
.
– Iceland_jack
Jul 9 at 18:18
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/4.0/"u003ecc by-sa 4.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%2f56911322%2fhow-to-extend-classes-in-haskell%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
As far as I know, the closest you can get in standard Haskell is
instance IsEven Int where
isEven n = n `divisibleBy` 2
instance DivisibleBy Int where
divisibleBy a i = a `mod` i == 0
You don't have to duplicate the logic (indeed, you can implement isEven
in terms of divisibleBy
), but you still need to provide an explicit definition.
You would have to repeat this pattern for every type you want to make an instance of DivisibleBy
.
Using the DefaultSignatures
language extension you can also do the following:
-# LANGUAGE DefaultSignatures #-
class IsEven a where
isEven :: a -> Bool
default isEven :: (DivisibleBy a) => a -> Bool
isEven n = n `divisibleBy` 2
class (IsEven a) => DivisibleBy a where
divisibleBy :: a -> Int -> Bool
instance IsEven Int
instance DivisibleBy Int where
divisibleBy a i = a `mod` i == 0
This moves the default implementation to the class itself. Now you can indeed just say instance IsEven Int
without providing an instance body. The disadvantage is that now IsEven
has to know about DivisibleBy
, and you can only provide one default
implementation.
It might be worth it to explicitly note that theliftM
idiom (in this case, definingisEvenDivisible :: DivisibleBy a => a -> Bool
for the sake of writing boilerplateIsEven
instances) might make the Haskell 98 solution slightly more comfortable. (Ultimately, I guess, it is a matter of how many extra names is one willing to tolerate, versus how often one can stand rewriting the full boilerplate implementation.)
– duplode
Jul 6 at 11:02
add a comment
|
As far as I know, the closest you can get in standard Haskell is
instance IsEven Int where
isEven n = n `divisibleBy` 2
instance DivisibleBy Int where
divisibleBy a i = a `mod` i == 0
You don't have to duplicate the logic (indeed, you can implement isEven
in terms of divisibleBy
), but you still need to provide an explicit definition.
You would have to repeat this pattern for every type you want to make an instance of DivisibleBy
.
Using the DefaultSignatures
language extension you can also do the following:
-# LANGUAGE DefaultSignatures #-
class IsEven a where
isEven :: a -> Bool
default isEven :: (DivisibleBy a) => a -> Bool
isEven n = n `divisibleBy` 2
class (IsEven a) => DivisibleBy a where
divisibleBy :: a -> Int -> Bool
instance IsEven Int
instance DivisibleBy Int where
divisibleBy a i = a `mod` i == 0
This moves the default implementation to the class itself. Now you can indeed just say instance IsEven Int
without providing an instance body. The disadvantage is that now IsEven
has to know about DivisibleBy
, and you can only provide one default
implementation.
It might be worth it to explicitly note that theliftM
idiom (in this case, definingisEvenDivisible :: DivisibleBy a => a -> Bool
for the sake of writing boilerplateIsEven
instances) might make the Haskell 98 solution slightly more comfortable. (Ultimately, I guess, it is a matter of how many extra names is one willing to tolerate, versus how often one can stand rewriting the full boilerplate implementation.)
– duplode
Jul 6 at 11:02
add a comment
|
As far as I know, the closest you can get in standard Haskell is
instance IsEven Int where
isEven n = n `divisibleBy` 2
instance DivisibleBy Int where
divisibleBy a i = a `mod` i == 0
You don't have to duplicate the logic (indeed, you can implement isEven
in terms of divisibleBy
), but you still need to provide an explicit definition.
You would have to repeat this pattern for every type you want to make an instance of DivisibleBy
.
Using the DefaultSignatures
language extension you can also do the following:
-# LANGUAGE DefaultSignatures #-
class IsEven a where
isEven :: a -> Bool
default isEven :: (DivisibleBy a) => a -> Bool
isEven n = n `divisibleBy` 2
class (IsEven a) => DivisibleBy a where
divisibleBy :: a -> Int -> Bool
instance IsEven Int
instance DivisibleBy Int where
divisibleBy a i = a `mod` i == 0
This moves the default implementation to the class itself. Now you can indeed just say instance IsEven Int
without providing an instance body. The disadvantage is that now IsEven
has to know about DivisibleBy
, and you can only provide one default
implementation.
As far as I know, the closest you can get in standard Haskell is
instance IsEven Int where
isEven n = n `divisibleBy` 2
instance DivisibleBy Int where
divisibleBy a i = a `mod` i == 0
You don't have to duplicate the logic (indeed, you can implement isEven
in terms of divisibleBy
), but you still need to provide an explicit definition.
You would have to repeat this pattern for every type you want to make an instance of DivisibleBy
.
Using the DefaultSignatures
language extension you can also do the following:
-# LANGUAGE DefaultSignatures #-
class IsEven a where
isEven :: a -> Bool
default isEven :: (DivisibleBy a) => a -> Bool
isEven n = n `divisibleBy` 2
class (IsEven a) => DivisibleBy a where
divisibleBy :: a -> Int -> Bool
instance IsEven Int
instance DivisibleBy Int where
divisibleBy a i = a `mod` i == 0
This moves the default implementation to the class itself. Now you can indeed just say instance IsEven Int
without providing an instance body. The disadvantage is that now IsEven
has to know about DivisibleBy
, and you can only provide one default
implementation.
edited Jul 6 at 5:16
answered Jul 6 at 5:06
melpomenemelpomene
75k6 gold badges58 silver badges110 bronze badges
75k6 gold badges58 silver badges110 bronze badges
It might be worth it to explicitly note that theliftM
idiom (in this case, definingisEvenDivisible :: DivisibleBy a => a -> Bool
for the sake of writing boilerplateIsEven
instances) might make the Haskell 98 solution slightly more comfortable. (Ultimately, I guess, it is a matter of how many extra names is one willing to tolerate, versus how often one can stand rewriting the full boilerplate implementation.)
– duplode
Jul 6 at 11:02
add a comment
|
It might be worth it to explicitly note that theliftM
idiom (in this case, definingisEvenDivisible :: DivisibleBy a => a -> Bool
for the sake of writing boilerplateIsEven
instances) might make the Haskell 98 solution slightly more comfortable. (Ultimately, I guess, it is a matter of how many extra names is one willing to tolerate, versus how often one can stand rewriting the full boilerplate implementation.)
– duplode
Jul 6 at 11:02
It might be worth it to explicitly note that the
liftM
idiom (in this case, defining isEvenDivisible :: DivisibleBy a => a -> Bool
for the sake of writing boilerplate IsEven
instances) might make the Haskell 98 solution slightly more comfortable. (Ultimately, I guess, it is a matter of how many extra names is one willing to tolerate, versus how often one can stand rewriting the full boilerplate implementation.)– duplode
Jul 6 at 11:02
It might be worth it to explicitly note that the
liftM
idiom (in this case, defining isEvenDivisible :: DivisibleBy a => a -> Bool
for the sake of writing boilerplate IsEven
instances) might make the Haskell 98 solution slightly more comfortable. (Ultimately, I guess, it is a matter of how many extra names is one willing to tolerate, versus how often one can stand rewriting the full boilerplate implementation.)– duplode
Jul 6 at 11:02
add a comment
|
You can't redefine a method in a new class and have it affect the one in the old class. If you want methods to work like this, the parent class has to reference the child class.
You need the DefaultSignatures
extension to make this work. Turn it on and then change your classes to this:
class IsEven a where
isEven :: a -> Bool
default isEven :: DivisibleBy a => a -> Bool
isEven a = divisibleBy a 2
class IsEven a => DivisibleBy a where
divisibleBy :: a -> Int -> Bool
hm, but what if then there is some other type class which also has enough functionality to implementisEven
? It seems like doing it this way requires the superclass to "know about" its subclass. In Scala, for example, you can subclass any typeclass and provide implementations for the superclasses methods. Eg,Applicative
can implementFunctor
'smap
usingap
: github.com/typelevel/cats/blob/master/core/src/main/scala/cats/… Is there a way to achieve something similar in Haskell? In Scala, if there is anApplicative
in scope, you get aFunctor
for free.
– David Abrahams
Jul 6 at 5:09
@DavidAbrahams I don't believe that Haskell currently has any equivalent to that functionality.
– Joseph Sible
Jul 6 at 5:11
add a comment
|
You can't redefine a method in a new class and have it affect the one in the old class. If you want methods to work like this, the parent class has to reference the child class.
You need the DefaultSignatures
extension to make this work. Turn it on and then change your classes to this:
class IsEven a where
isEven :: a -> Bool
default isEven :: DivisibleBy a => a -> Bool
isEven a = divisibleBy a 2
class IsEven a => DivisibleBy a where
divisibleBy :: a -> Int -> Bool
hm, but what if then there is some other type class which also has enough functionality to implementisEven
? It seems like doing it this way requires the superclass to "know about" its subclass. In Scala, for example, you can subclass any typeclass and provide implementations for the superclasses methods. Eg,Applicative
can implementFunctor
'smap
usingap
: github.com/typelevel/cats/blob/master/core/src/main/scala/cats/… Is there a way to achieve something similar in Haskell? In Scala, if there is anApplicative
in scope, you get aFunctor
for free.
– David Abrahams
Jul 6 at 5:09
@DavidAbrahams I don't believe that Haskell currently has any equivalent to that functionality.
– Joseph Sible
Jul 6 at 5:11
add a comment
|
You can't redefine a method in a new class and have it affect the one in the old class. If you want methods to work like this, the parent class has to reference the child class.
You need the DefaultSignatures
extension to make this work. Turn it on and then change your classes to this:
class IsEven a where
isEven :: a -> Bool
default isEven :: DivisibleBy a => a -> Bool
isEven a = divisibleBy a 2
class IsEven a => DivisibleBy a where
divisibleBy :: a -> Int -> Bool
You can't redefine a method in a new class and have it affect the one in the old class. If you want methods to work like this, the parent class has to reference the child class.
You need the DefaultSignatures
extension to make this work. Turn it on and then change your classes to this:
class IsEven a where
isEven :: a -> Bool
default isEven :: DivisibleBy a => a -> Bool
isEven a = divisibleBy a 2
class IsEven a => DivisibleBy a where
divisibleBy :: a -> Int -> Bool
answered Jul 6 at 5:02
Joseph SibleJoseph Sible
14.7k3 gold badges20 silver badges52 bronze badges
14.7k3 gold badges20 silver badges52 bronze badges
hm, but what if then there is some other type class which also has enough functionality to implementisEven
? It seems like doing it this way requires the superclass to "know about" its subclass. In Scala, for example, you can subclass any typeclass and provide implementations for the superclasses methods. Eg,Applicative
can implementFunctor
'smap
usingap
: github.com/typelevel/cats/blob/master/core/src/main/scala/cats/… Is there a way to achieve something similar in Haskell? In Scala, if there is anApplicative
in scope, you get aFunctor
for free.
– David Abrahams
Jul 6 at 5:09
@DavidAbrahams I don't believe that Haskell currently has any equivalent to that functionality.
– Joseph Sible
Jul 6 at 5:11
add a comment
|
hm, but what if then there is some other type class which also has enough functionality to implementisEven
? It seems like doing it this way requires the superclass to "know about" its subclass. In Scala, for example, you can subclass any typeclass and provide implementations for the superclasses methods. Eg,Applicative
can implementFunctor
'smap
usingap
: github.com/typelevel/cats/blob/master/core/src/main/scala/cats/… Is there a way to achieve something similar in Haskell? In Scala, if there is anApplicative
in scope, you get aFunctor
for free.
– David Abrahams
Jul 6 at 5:09
@DavidAbrahams I don't believe that Haskell currently has any equivalent to that functionality.
– Joseph Sible
Jul 6 at 5:11
hm, but what if then there is some other type class which also has enough functionality to implement
isEven
? It seems like doing it this way requires the superclass to "know about" its subclass. In Scala, for example, you can subclass any typeclass and provide implementations for the superclasses methods. Eg, Applicative
can implement Functor
's map
using ap
: github.com/typelevel/cats/blob/master/core/src/main/scala/cats/… Is there a way to achieve something similar in Haskell? In Scala, if there is an Applicative
in scope, you get a Functor
for free.– David Abrahams
Jul 6 at 5:09
hm, but what if then there is some other type class which also has enough functionality to implement
isEven
? It seems like doing it this way requires the superclass to "know about" its subclass. In Scala, for example, you can subclass any typeclass and provide implementations for the superclasses methods. Eg, Applicative
can implement Functor
's map
using ap
: github.com/typelevel/cats/blob/master/core/src/main/scala/cats/… Is there a way to achieve something similar in Haskell? In Scala, if there is an Applicative
in scope, you get a Functor
for free.– David Abrahams
Jul 6 at 5:09
@DavidAbrahams I don't believe that Haskell currently has any equivalent to that functionality.
– Joseph Sible
Jul 6 at 5:11
@DavidAbrahams I don't believe that Haskell currently has any equivalent to that functionality.
– Joseph Sible
Jul 6 at 5:11
add a comment
|
With GHC 8.6 and above, this can also be achieved through DerivingVia
:
-# LANGUAGE DerivingVia #-
-# LANGUAGE GeneralisedNewtypeDeriving #-
-# LANGUAGE StandaloneDeriving #-
-- Class definitions:
class IsEven a where
isEven :: a -> Bool
-- Note that we don't need to have IsEven as a superclass.
class DivisibleBy a where
divisibleBy :: a -> Int -> Bool
-- Boilerplate that only needs to be written once:
-- Boilerplate DivisibleBy instance generated with GeneralisedNewtypeDeriving.
newtype WrappedDivisibleBy a = WrapDivisibleBy unwrapDivisibleBy :: a
deriving DivisibleBy
instance DivisibleBy a => IsEven (WrappedDivisibleBy a) where
isEven n = n `divisibleBy` 2
-- Instance example:
instance DivisibleBy Int where
divisibleBy a i = a `mod` i == 0
-- Boilerplate IsEven instance generated with DerivingVia
-- (and StandaloneDeriving, as we aren't defining Int here).
deriving via (WrappedDivisibleBy Int) instance IsEven Int
DerivingVia
is not always an option (in the case of classes like Traversable
, which have an extra type constructor wrapping things in the type signature, it clashes with the role system); when it works, though, it is very neat.
Ah this is neat! I actually don't think we needDivisibleBy
to extendisEven
in this case, correct? (It compiles without it)
– David Abrahams
Jul 6 at 21:45
1
@DavidAbrahams We don't, indeed. I have updated the answer to reflect that. (While here I'm primarily dealing with the typeclass mechanics, chepner does have a point about this specific example, so dropping the relationship looks like a net gain as far as this answer is concerned.)
– duplode
Jul 6 at 21:53
1
I always wantedunsafe via
to bypass the role system, it would useunsafeCoerce
instead ofcoerce
– Iceland_jack
Jul 9 at 14:29
@Iceland_jack Tangential question: will theQuantifiedConstraints
approach discussed in this ticket allow derivingTraversable
andDistributive
? Though I might well be reading the situation wrongly, it looks like that would demand coercing under an arbitrary functor that the instance implementer has no control over.
– duplode
Jul 9 at 16:08
1
Not as smoothly asjoin
, requires big changes but I think it is the best path forward, theforall f a b. Applicative f =>
quantification appears in the type oftraverse @t @f @a..
, we can only work witht
. Either strengthenApplicative f
withRepresentational1
or decree allFunctor
s be representational in their first arg by makingclass Representational1 f => Functor f
a superclass. Ryan explains better:QuantifiedConstraints
and the trouble withTraversable
.
– Iceland_jack
Jul 9 at 18:18
add a comment
|
With GHC 8.6 and above, this can also be achieved through DerivingVia
:
-# LANGUAGE DerivingVia #-
-# LANGUAGE GeneralisedNewtypeDeriving #-
-# LANGUAGE StandaloneDeriving #-
-- Class definitions:
class IsEven a where
isEven :: a -> Bool
-- Note that we don't need to have IsEven as a superclass.
class DivisibleBy a where
divisibleBy :: a -> Int -> Bool
-- Boilerplate that only needs to be written once:
-- Boilerplate DivisibleBy instance generated with GeneralisedNewtypeDeriving.
newtype WrappedDivisibleBy a = WrapDivisibleBy unwrapDivisibleBy :: a
deriving DivisibleBy
instance DivisibleBy a => IsEven (WrappedDivisibleBy a) where
isEven n = n `divisibleBy` 2
-- Instance example:
instance DivisibleBy Int where
divisibleBy a i = a `mod` i == 0
-- Boilerplate IsEven instance generated with DerivingVia
-- (and StandaloneDeriving, as we aren't defining Int here).
deriving via (WrappedDivisibleBy Int) instance IsEven Int
DerivingVia
is not always an option (in the case of classes like Traversable
, which have an extra type constructor wrapping things in the type signature, it clashes with the role system); when it works, though, it is very neat.
Ah this is neat! I actually don't think we needDivisibleBy
to extendisEven
in this case, correct? (It compiles without it)
– David Abrahams
Jul 6 at 21:45
1
@DavidAbrahams We don't, indeed. I have updated the answer to reflect that. (While here I'm primarily dealing with the typeclass mechanics, chepner does have a point about this specific example, so dropping the relationship looks like a net gain as far as this answer is concerned.)
– duplode
Jul 6 at 21:53
1
I always wantedunsafe via
to bypass the role system, it would useunsafeCoerce
instead ofcoerce
– Iceland_jack
Jul 9 at 14:29
@Iceland_jack Tangential question: will theQuantifiedConstraints
approach discussed in this ticket allow derivingTraversable
andDistributive
? Though I might well be reading the situation wrongly, it looks like that would demand coercing under an arbitrary functor that the instance implementer has no control over.
– duplode
Jul 9 at 16:08
1
Not as smoothly asjoin
, requires big changes but I think it is the best path forward, theforall f a b. Applicative f =>
quantification appears in the type oftraverse @t @f @a..
, we can only work witht
. Either strengthenApplicative f
withRepresentational1
or decree allFunctor
s be representational in their first arg by makingclass Representational1 f => Functor f
a superclass. Ryan explains better:QuantifiedConstraints
and the trouble withTraversable
.
– Iceland_jack
Jul 9 at 18:18
add a comment
|
With GHC 8.6 and above, this can also be achieved through DerivingVia
:
-# LANGUAGE DerivingVia #-
-# LANGUAGE GeneralisedNewtypeDeriving #-
-# LANGUAGE StandaloneDeriving #-
-- Class definitions:
class IsEven a where
isEven :: a -> Bool
-- Note that we don't need to have IsEven as a superclass.
class DivisibleBy a where
divisibleBy :: a -> Int -> Bool
-- Boilerplate that only needs to be written once:
-- Boilerplate DivisibleBy instance generated with GeneralisedNewtypeDeriving.
newtype WrappedDivisibleBy a = WrapDivisibleBy unwrapDivisibleBy :: a
deriving DivisibleBy
instance DivisibleBy a => IsEven (WrappedDivisibleBy a) where
isEven n = n `divisibleBy` 2
-- Instance example:
instance DivisibleBy Int where
divisibleBy a i = a `mod` i == 0
-- Boilerplate IsEven instance generated with DerivingVia
-- (and StandaloneDeriving, as we aren't defining Int here).
deriving via (WrappedDivisibleBy Int) instance IsEven Int
DerivingVia
is not always an option (in the case of classes like Traversable
, which have an extra type constructor wrapping things in the type signature, it clashes with the role system); when it works, though, it is very neat.
With GHC 8.6 and above, this can also be achieved through DerivingVia
:
-# LANGUAGE DerivingVia #-
-# LANGUAGE GeneralisedNewtypeDeriving #-
-# LANGUAGE StandaloneDeriving #-
-- Class definitions:
class IsEven a where
isEven :: a -> Bool
-- Note that we don't need to have IsEven as a superclass.
class DivisibleBy a where
divisibleBy :: a -> Int -> Bool
-- Boilerplate that only needs to be written once:
-- Boilerplate DivisibleBy instance generated with GeneralisedNewtypeDeriving.
newtype WrappedDivisibleBy a = WrapDivisibleBy unwrapDivisibleBy :: a
deriving DivisibleBy
instance DivisibleBy a => IsEven (WrappedDivisibleBy a) where
isEven n = n `divisibleBy` 2
-- Instance example:
instance DivisibleBy Int where
divisibleBy a i = a `mod` i == 0
-- Boilerplate IsEven instance generated with DerivingVia
-- (and StandaloneDeriving, as we aren't defining Int here).
deriving via (WrappedDivisibleBy Int) instance IsEven Int
DerivingVia
is not always an option (in the case of classes like Traversable
, which have an extra type constructor wrapping things in the type signature, it clashes with the role system); when it works, though, it is very neat.
edited Jul 6 at 21:54
answered Jul 6 at 11:30
duplodeduplode
27.2k6 gold badges59 silver badges108 bronze badges
27.2k6 gold badges59 silver badges108 bronze badges
Ah this is neat! I actually don't think we needDivisibleBy
to extendisEven
in this case, correct? (It compiles without it)
– David Abrahams
Jul 6 at 21:45
1
@DavidAbrahams We don't, indeed. I have updated the answer to reflect that. (While here I'm primarily dealing with the typeclass mechanics, chepner does have a point about this specific example, so dropping the relationship looks like a net gain as far as this answer is concerned.)
– duplode
Jul 6 at 21:53
1
I always wantedunsafe via
to bypass the role system, it would useunsafeCoerce
instead ofcoerce
– Iceland_jack
Jul 9 at 14:29
@Iceland_jack Tangential question: will theQuantifiedConstraints
approach discussed in this ticket allow derivingTraversable
andDistributive
? Though I might well be reading the situation wrongly, it looks like that would demand coercing under an arbitrary functor that the instance implementer has no control over.
– duplode
Jul 9 at 16:08
1
Not as smoothly asjoin
, requires big changes but I think it is the best path forward, theforall f a b. Applicative f =>
quantification appears in the type oftraverse @t @f @a..
, we can only work witht
. Either strengthenApplicative f
withRepresentational1
or decree allFunctor
s be representational in their first arg by makingclass Representational1 f => Functor f
a superclass. Ryan explains better:QuantifiedConstraints
and the trouble withTraversable
.
– Iceland_jack
Jul 9 at 18:18
add a comment
|
Ah this is neat! I actually don't think we needDivisibleBy
to extendisEven
in this case, correct? (It compiles without it)
– David Abrahams
Jul 6 at 21:45
1
@DavidAbrahams We don't, indeed. I have updated the answer to reflect that. (While here I'm primarily dealing with the typeclass mechanics, chepner does have a point about this specific example, so dropping the relationship looks like a net gain as far as this answer is concerned.)
– duplode
Jul 6 at 21:53
1
I always wantedunsafe via
to bypass the role system, it would useunsafeCoerce
instead ofcoerce
– Iceland_jack
Jul 9 at 14:29
@Iceland_jack Tangential question: will theQuantifiedConstraints
approach discussed in this ticket allow derivingTraversable
andDistributive
? Though I might well be reading the situation wrongly, it looks like that would demand coercing under an arbitrary functor that the instance implementer has no control over.
– duplode
Jul 9 at 16:08
1
Not as smoothly asjoin
, requires big changes but I think it is the best path forward, theforall f a b. Applicative f =>
quantification appears in the type oftraverse @t @f @a..
, we can only work witht
. Either strengthenApplicative f
withRepresentational1
or decree allFunctor
s be representational in their first arg by makingclass Representational1 f => Functor f
a superclass. Ryan explains better:QuantifiedConstraints
and the trouble withTraversable
.
– Iceland_jack
Jul 9 at 18:18
Ah this is neat! I actually don't think we need
DivisibleBy
to extend isEven
in this case, correct? (It compiles without it)– David Abrahams
Jul 6 at 21:45
Ah this is neat! I actually don't think we need
DivisibleBy
to extend isEven
in this case, correct? (It compiles without it)– David Abrahams
Jul 6 at 21:45
1
1
@DavidAbrahams We don't, indeed. I have updated the answer to reflect that. (While here I'm primarily dealing with the typeclass mechanics, chepner does have a point about this specific example, so dropping the relationship looks like a net gain as far as this answer is concerned.)
– duplode
Jul 6 at 21:53
@DavidAbrahams We don't, indeed. I have updated the answer to reflect that. (While here I'm primarily dealing with the typeclass mechanics, chepner does have a point about this specific example, so dropping the relationship looks like a net gain as far as this answer is concerned.)
– duplode
Jul 6 at 21:53
1
1
I always wanted
unsafe via
to bypass the role system, it would use unsafeCoerce
instead of coerce
– Iceland_jack
Jul 9 at 14:29
I always wanted
unsafe via
to bypass the role system, it would use unsafeCoerce
instead of coerce
– Iceland_jack
Jul 9 at 14:29
@Iceland_jack Tangential question: will the
QuantifiedConstraints
approach discussed in this ticket allow deriving Traversable
and Distributive
? Though I might well be reading the situation wrongly, it looks like that would demand coercing under an arbitrary functor that the instance implementer has no control over.– duplode
Jul 9 at 16:08
@Iceland_jack Tangential question: will the
QuantifiedConstraints
approach discussed in this ticket allow deriving Traversable
and Distributive
? Though I might well be reading the situation wrongly, it looks like that would demand coercing under an arbitrary functor that the instance implementer has no control over.– duplode
Jul 9 at 16:08
1
1
Not as smoothly as
join
, requires big changes but I think it is the best path forward, the forall f a b. Applicative f =>
quantification appears in the type of traverse @t @f @a..
, we can only work with t
. Either strengthen Applicative f
with Representational1
or decree all Functor
s be representational in their first arg by making class Representational1 f => Functor f
a superclass. Ryan explains better: QuantifiedConstraints
and the trouble with Traversable
.– Iceland_jack
Jul 9 at 18:18
Not as smoothly as
join
, requires big changes but I think it is the best path forward, the forall f a b. Applicative f =>
quantification appears in the type of traverse @t @f @a..
, we can only work with t
. Either strengthen Applicative f
with Representational1
or decree all Functor
s be representational in their first arg by making class Representational1 f => Functor f
a superclass. Ryan explains better: QuantifiedConstraints
and the trouble with Traversable
.– Iceland_jack
Jul 9 at 18:18
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%2f56911322%2fhow-to-extend-classes-in-haskell%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
3
There is no reason for
DivisibleBy
to be a subclass ofIsEven
. The opposite relation is reasonable, as is making the two classes independent. Just because usingdivisibleBy
is one way to defineisEven
doesn't mean anIsEven
instance should be required.– chepner
Jul 6 at 14:34