Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • Home
  • SEARCH
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 7165283
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 28, 20262026-05-28T14:12:50+00:00 2026-05-28T14:12:50+00:00

I think I may have asked this on Haskell-Cafe at some point, but damned

  • 0

I think I may have asked this on Haskell-Cafe at some point, but damned if I can find the answer now… So I’m asking it again here, so hopefully in future I can find the answer!

Haskell is fantastic at dealing with parametric polymorphism. But the trouble is that not everything is parametric. As a trivial example, suppose we want to fetch the first element of data out of a container. For a parametric type, that’s trivial:

class HasFirst c where
  first :: c x -> Maybe x

instance HasFirst [] where
  first []    = Nothing
  first (x:_) = Just x

Now try and write an instance for ByteString. You can’t. Its type doesn’t mention the element type. You also cannot write an instance for Set, because it requires an Ord constraint – but the class head doesn’t mention the element type, so you cannot constrain it.

Associated types provide a neat way to completely fix these problems:

class HasFirst c where
  type Element c :: *
  first :: c -> Maybe (Element c)

instance HasFirst [x] where
  type Element [x] = x
  first []    = Nothing
  first (x:_) = Just x

instance HasFirst ByteString where
  type Element ByteString = Word8
  first b = b ! 0

instance Ord x => HasFirst (Set x) where
  type Element (Set x) = x
  first s = findMin s

We now have a new problem, however. Consider trying to “fix” Functor so that it works for all container types:

class Functor f where
  type Element f :: *
  fmap :: (Functor f2) => (Element f -> Element f2) -> f -> f2

This doesn’t work at all. It says that if we have a function from the element type of f to the element type of f2, then we can turn an f into an f2. So far so good. However, there is apparently no way to demand that f and f2 are the same sort of container!

Under the existing Functor definition, we have

fmap :: (x -> y) -> [x] -> [y]
fmap :: (x -> y) -> Seq x -> Seq y
fmap :: (x -> y) -> IO x -> IO y

But we do not have fmap :: (x -> y) -> IO x -> [y]. That is quite impossible. But the class definition above allows it.

Does anybody know how to explain to the type system what I actually meant?

Edit

The above works by defining a way to compute an element type from a container type. What happens if you try to do it the other way around? Define a function to compute a container type from an element type? Does that work out any easier?

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-28T14:12:51+00:00Added an answer on May 28, 2026 at 2:12 pm

    Well, the problem is that it’s not clear what the revised Functor is supposed to mean. For instance, consider ByteString. A ByteString can only be mapped by replacing each Word8 element with an element of the same type. But Functor is for parametric mappable structures. There are really two conflicting notions of mapping here:

    • Rigid mapping (i.e. transforming each element of a structure without changing its type)
    • Parametric mapping (i.e. transforming each element of a structure to any type)

    So, in this case, you can’t explain to the type system what you meant, because it doesn’t make much sense. You can, however, change what you mean 🙂

    Rigid mapping is easy to express with type families:

    class RigidMap f where
      type Element f :: *
      rigidMap :: (Element f -> Element f) -> f -> f
    

    As far as parametric mapping, there are multiple ways to do it. The simplest way would be to retain the current Functor as-is. Together, these classes covers structures like ByteString, [], Seq, and so on. However, they all fall down on Set and Map, because of the Ord constraint on values. Happily, the constraint kinds extension coming in GHC 7.4 lets us fix this problem:

    class RFunctor f where
      type Element f a :: Constraint
      type Element f a = ()  -- default empty constraint
      fmap :: (Element f a, Element f b) => (a -> b) -> f a -> f b
    

    Here, we’re saying that every instance should have an associated typeclass constraint. For instance, the Set instance will have Element Set a = Ord a, to denote that Sets can only be constructed if an Ord instance is available for the type. Anything that can appear on the left hand of => is accepted. We can define our previous instances exactly as they were, but we can also do Set and Map:

    instance RFunctor Set where
      type Element Set a = Ord a
      fmap = Set.map
    
    instance RFunctor Map where
      type Element Map a = Ord a
      fmap = Map.map
    

    However, it’s pretty annoying to have to use two separate interfaces for rigid mapping and restricted parametric mapping. In fact, isn’t the latter a generalisation of the former? Consider the difference between Set, which can only contain instances of Ord, and ByteString, which can only contain Word8s. Surely we can express that as just another constraint?

    We apply the same trick done to HasFirst (i.e. give instances for the whole structure and use a type family to expose the element type), and introduce a new associated constraint family:

    class Mappable f where
      type Element f :: *
      type Result f a r :: Constraint
      map :: (Result f a r) => (Element f -> a) -> f -> r
    

    The idea here is that Result f a r expresses the constraints it needs on the value type (like Ord a), and also constrains the resulting container type however it needs to; presumably, to ensure it has the type of a same-sort-of-container of as. For instance, Result [a] b r will presumably require that r is [b], and Result ByteString b r will require that b is Word8, and r is ByteString.

    Type families already give us what we need to express “is” here: a type equality constraint. We can say (a ~ b) => ... to require that a and b are the same type. We can, of course, use this in constraint family definitions. So, we have everything we need; on to the instances:

    instance Mappable [a] where
      type Element [a] = a
      type Result [a] b r = r ~ [b]
      -- The type in this case works out as:
      --   map :: (r ~ [b]) => (a -> b) -> [a] -> r
      -- which simplifies to:
      --   map :: (a -> b) -> [a] -> [b]
      map = Prelude.map
    
    instance Mappable ByteString where
      type Element ByteString = Word8
      type Result ByteString a r = (a ~ Word8, r ~ ByteString)
      -- The type is:
      --   map :: (b ~ Word8, r ~ ByteString) => (Word8 -> b) -> ByteString -> r
      -- which simplifies to:
      --   map :: (Word8 -> Word8) -> ByteString -> ByteString
      map = ByteString.map
    
    instance (Ord a) => Mappable (Set a) where
      type Element (Set a) = a
      type Result (Set a) b r = (Ord b, r ~ Set b)
      -- The type is:
      --   map :: (Ord a, Ord b, r ~ Set b) => (a -> b) -> Set a -> r
      -- (note the (Ord a) constraint from the instance head)
      -- which simplifies to:
      --   map :: (Ord a, Ord b) => (a -> b) -> Set a -> Set b
      map = Set.map
    

    Perfect! We can define instances for any type of container we want, rigid, parametric or parametric-but-restricted, and the types work out perfectly.

    Disclaimer: I haven’t tried GHC 7.4 yet, so I don’t know if any of this actually compiles or works, but I think the basic ideas are sound.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

This question may have been asked already - but unfortunately, I could not find
I asked this question yesterday got a pretty good answer but can't figure out
I know this has been asked before but I can't find it so... Say
You may think this question is like this question asked on StackOverflow earlier. But
(I think this question has been asked before, but the answers have been contradictory
This may have been asked a lot but I'm still lost. I need to
I think we may have trouble with our existing project. For some reasons we
I think this may be relatively straight forward. I have a rewrite rule that
Hopefully this question isn't as subjective as I think it may be. I have
It may seem to be asked several times, but I could not find answers

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.