3

I have a datatype which keeps track of a 'block' of numbers (kinda like a matrix).

newtype Block a = Block [[a]]

I would like to make it an instance of Functor. However, I am trying to do it in such a way that fmap can apply to the whole list block, so that fmap has type fmap :: [[a]] -> [[b]] instead of type fmap :: a -> b.

The reason is because I would like to map into my Block functor functions like transpose which apply to the list block [[a]] not to each element a. That is, I would like to be able to define functions like

transposeBlock :: Block a -> Block a
transposeBlock = (transpose <$>)

I tried to declare my functor instance as the following.

instance Functor (Block [[a]]) where
 fmap f (Block x) = Block (f x)

But I ran into the type errors that arise from attempting to compile this.

error:
• Expecting one fewer argument to ‘Block [[a]]’
 Expected kind ‘* -> *’, but ‘Block [[a]]’ has kind ‘*’
• In the first argument of ‘Functor’, namely ‘Block [[a]]’
 In the instance declaration for ‘Functor (Block [[a]])’

What are some ways that I could map functions into the list block [[a]] of my Block type?

asked Dec 7, 2017 at 20:44
2
  • functor really can't do this, due to it's nature. Commented Dec 7, 2017 at 21:33
  • 2
    Why not fmap . fmap :: (a -> b) -> [[a]] -> [[b]]? Commented Dec 7, 2017 at 22:13

3 Answers 3

9

Sorry, you can't call that function fmap. But that's okay, there's lots of other good names out there.

onBlock :: ([[a]] -> [[b]]) -> Block a -> Block b
onBlock f (Block v) = Block (f v)

You can even give it a name full of infixy punctuation goodness if you want.

(<#>) :: ([[a]] -> [[b]]) -> Block a -> Block b
f <#> Block v = Block (f v)
answered Dec 7, 2017 at 20:52
Sign up to request clarification or add additional context in comments.

4 Comments

Is there really no way to do this with an fmap? I jumped on this question quickly, thinking it was easy to fix. Then it didn't sit right and I tried it quickly and couldn't get it to work. Is it because of the kind of Functor hiding the a in your instance declaration?
@jkeuhlen You can certainly make Block an instance of Functor; e.g. instance Functor Block where fmap f (Block x) = Block (fmap (fmap f) x). But the type of fmap doesn't unify with the type of onBlock, so using onBlock as the implementation is a non-starter.
Ahh right, because the a in fmap :: (a -> b) -> f a -> f b will never unify with [[a]]. Is my understanding correct?
@jkeuhlen, kind of? Basically, the signature of fmap is short for forall a b. (a -> b) -> f a -> f b, in other words, it has to work for any types a and b -- including ones which are not lists. An "fmap" that does not do this doesn't count as a functor -- for example, the generic function foo x = fmap (+1) x should work if x is an f Int, but it wouldn't work on Block Int if you got your way.
3

Others have already commented on the opportunity of using fmap for this.

Wrapping / unwrapping newtypes is boring, but safe coercions can make this immediate.

import Data.Coerce
newtype Block a = Block [[a]]
onBlock :: ([[a]] -> [[b]]) -> Block a -> Block b
onBlock = coerce

Indeed, one might even avoid onBlock completely, and directly use coerce as needed.

answered Dec 7, 2017 at 22:11

2 Comments

Nice, I'm ashamed I didn't think of this myself! Though I disagree, in that I bet onBlock is still worth defining -- every time I have tried to use coerce bare, I end up drowning in type annotations.
@DanielWagner That's true! coerce is powerful, but sometimes it needs to be restricted.
2

The easiest way to achieve that would be

newtype BlockWr a = Block a deriving (Functor)
type Block a = BlockWr [[a]]

Now transposeBlock = (transpose <$>) would work.

But I wouldn't recommend this. BlockWr is just the identity functor, this is not very useful.

Really, the point is that Block a is isomorphic to [[a]]. You can witness that:

{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
import Control.Lens.TH
newtype Block a = Block {_blockMat::[[a]]}
makeLenses ''Block
transposeBlock :: Block a -> Block a
transposeBlock = blockMat %~ transpose
answered Dec 7, 2017 at 21:46

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.