haskellnewtype

How to iterate through `newtype` List in Haskell


For that newtype is treated as a whole different type in the type system, I'm wondering if there is any way to use pattern matching or iterate a list with newtype, as follow.

newtype Foo = Foo [Int]

bar :: Foo -> Int
bar (x : xs) = x + bar xs
bar [] = 0

Solution

  • There are multiple options.

    1. Just manually wrap/unwrap the newtype right in place.

      bar (Foo (x : xs)) = x + bar (Foo xs)
      bar (Foo []) = 0
      
    2. Implement the function on lists, and unwrap it once before passing to the function. In this case the list version is just sum, so we can use

      bar (Foo xs) = sum xs
      
    3. Make an interface that allows to manipulate Foo values as if they were lists.

      {-# LANGUAGE PatternSynonyms #-}
      
      {-# COMPLETE (:%), FooNil #-}
      
      pattern (:%) :: Int -> Foo -> Foo
      pattern x :% xs <- Foo (x : (Foo -> xs))
       where x :% xs = Foo (x : getFoo xs)
      
      pattern FooNil :: Foo
      pattern FooNil = Foo []
      
      bar :: Foo -> Int
      bar (x :% xs) = x + bar xs
      bar FooNil = 0
      
    4. Abstract. You don't really need the particular list deconstructors, you just need some way to implement a fold over the contained data. There is a standard Foldable class in base for just this, but it would require you container to be parametric over the contained type. Since it's not parametric, you need to use MonoFoldable class from the mono-traversable package instead.

      import Data.MonoTraversable
      
      type instance Element Foo = Int
      import MonoFoldable Foo where
        ofoldr f (Foo (x:xs)) e = f x $ ofoldr f (Foo xs) e
        ofoldr _ (Foo []) e = e
      
      bar = ofoldr (+) 0
      

    Note that generally, this sort of function should be implemented with a strict left fold instead of a right fold.