Reputation: 6220
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?
Upvotes: 3
Views: 247
Reputation: 116174
Others have already commented on the opportunity of using fmap
for this.
Wrapping / unwrapping newtype
s 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.
Upvotes: 3
Reputation: 120751
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
Upvotes: 2
Reputation: 153332
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)
Upvotes: 9