Skip to content

Instantly share code, notes, and snippets.

@Sam-Serpoosh
Created March 3, 2015 22:39
Show Gist options
  • Save Sam-Serpoosh/4463277597a03b2b4341 to your computer and use it in GitHub Desktop.
Save Sam-Serpoosh/4463277597a03b2b4341 to your computer and use it in GitHub Desktop.
Type Deduction problem in Haskell (type class and instance declarations)
{-# LANGUAGE GeneralizedNewtypeDeriving, FlexibleInstances #-}
module Sized where
import Data.Monoid
newtype Size = Size { getSize :: Int }
deriving (Eq, Ord, Show, Num)
class Sized a where
size :: a -> Size
instance Sized Size where
size = id
instance Monoid Size where
mempty = Size 0
mappend = (+)
data JoinList m a = Empty
| Single m a
| Append m (JoinList m a) (JoinList m a)
deriving (Eq, Show)
testSomething :: (Sized b, Monoid b) => JoinList b a -> JoinList b a
testSomething (Single s value) = let numOfElem = getSize (size s)
newSize = Size (numOfElem + 1)
in Single newSize value
testSomething jl = jl
tree = Single (Size 5) "A"
main :: IO ()
main = putStrLn $ show $ testSomething tree
{-
Produces the following error:
Sized.hs:27:44:
Could not deduce (b ~ Size)
from the context (Sized b, Monoid b)
bound by the type signature for
testSomething :: (Sized b, Monoid b) =>
JoinList b a -> JoinList b a
at Sized.hs:24:18-68
‘b’ is a rigid type variable bound by
the type signature for
testSomething :: (Sized b, Monoid b) =>
JoinList b a -> JoinList b a
at Sized.hs:24:18
Relevant bindings include
s :: b (bound at Sized.hs:25:23)
testSomething :: JoinList b a -> JoinList b a
(bound at Sized.hs:25:1)
In the first argument of ‘Single’, namely ‘newSize’
In the expression: Single newSize value
-}
@Sam-Serpoosh
Copy link
Author

That Money example was FANTASTIC, it cleared all the confusion I had about this situation. The problem I had was that I tried to map this to the hierarchy of interface -> concrete implementation like the following Java code for instance:

public interface MyInterface { ... }

public class MyClass implements MyInterface { ... }

public MyInterface testSomething() {
  return new MyClass();
}

But that is the comparison of Apples and Oranges in this situation. In case of the Haskell code, I'm not specifying the return type as something that is Sized and Monoid which makes it ok to return a concrete data type which is an instance of both Monoid and Sized! I'm reaching into the parameterized datatype (e.g JoinList) and replacing the contained item (as you mentioned) with something more specific that it needs to be and I can end up with a situation like the Money example that you drew above! I put Money in the JoinList and get back Size in the JoinList which is not permitted in ANY language of course! That Java interface example distracted me from the root cause of the problem and I could never get out of that rabbit hole 😄

Thank you SO MUCH, I highly appreciate it. It was a very interesting discussion and I learned a bunch of cool things actually 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment