1
votes

I have newtype:

newtype Foo = Foo ([Int])

I would like to simply apply Int -> Int function over it like it is possible with fmap.

I thought it will be enough to derive or implement Functor instance, but it requires type of * -> * kind.

Is there some builtin way to make my type partially fmap-able?

2
Short answer: this type cannot be a Functor, as functors must have at least one type variable. For instance, newtype Foo' a = Foo' ([a]) would be a functor. However, even though this is not a functor, it is still entirely possible to write a mapping function foomap :: (Int -> Int) -> Foo -> Foo.bradrn

2 Answers

2
votes

Before getting carried away, you should keep in mind that you're trying to avoid (1) naming and (2) writing the monomorphic function:

mapFoo :: (Int -> Int) -> (Foo -> Foo)
mapFoo f (Foo ints) = Foo (f <$> ints)

If you really don't want to give this function a separate name and want GHC to write the function for you, I think the only sensible way is to re-define your type as a proper functor of kind * -> * with an automatically derived instance:

newtype FooF a = Foo [a] deriving (Functor)

and then define a type alias for the specialization to Int:

type Foo = FooF Int

This is not precisely equivalent to your original definition of Foo. In particular, the following expression in isolation:

Foo [1,2,3]

will have type Num a => FooF a instead of type Foo = FooF Int, so GHC may fail to infer types in all the places it used to. But, this Foo type alias will mostly behave like your original Foo newtype and will allow you to write:

fmap (*5) $ Foo [1,2,3]

and such.

On the other hand, if you want to keep your newtype the same, don't mind writing the function yourself, yet don't want to give that function a separate name, you can't use fmap (at least not without overriding the prelude definition, which kind of defeats the purpose). However, as per @leftroundabout's answer, you can use omap from the mono-traversable package. Because this requires you to define the function yourself in a MonoFunctor Foo instance (e.g., using the same definition as mapFoo above), there's no real point unless you're doing this for a bunch of non-functors besides Foo and want to use a single omap name for all of them or want to write functions that can handle any such MonoFunctor uniformly.

9
votes

https://hackage.haskell.org/package/mono-traversable-1.0.15.1/docs/Data-MonoTraversable.html#t:MonoFunctor

{-# LANGUAGE TypeFamilies      #-}

type instance Element Foo = Int

instance MonoFunctor Foo where
  -- omap :: (Int -> Int) -> Foo -> Foo
  omap = ...