3
votes

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?

3
functor really can't do this, due to it's nature.Jason Hu
Why not fmap . fmap :: (a -> b) -> [[a]] -> [[b]]?AJF

3 Answers

9
votes

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)
3
votes

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.

2
votes

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