Search code examples
haskelltypescoercion

Using newtype in Data.Array.Unboxed with ghc 7.10


This worked fine in ghc 7.8.4, but fails in 7.10.3:

{-# LANGUAGE DeriveGeneric              #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module Foo where

import qualified Data.Array.Unboxed       as A
import           GHC.Generics             (Generic)

newtype Elt = Elt Int deriving (Eq, Ord, Show, Num, Integral, Real, Enum, A.IArray A.UArray, Generic)
type MyArr = A.UArray Int Elt

with lots of messages like

/tmp/my.hs:9:75:
    Couldn't match type ‘Int’ with ‘Elt’
    arising from the coercion of the method ‘Data.Array.Base.numElements’
      from type ‘forall i. A.Ix i => A.UArray i Int -> Int’
        to type ‘forall i. A.Ix i => A.UArray i Elt -> Int’
    Relevant role signatures:
      type role A.Ix nominal
      type role A.UArray nominal nominal
    When deriving the instance for (A.IArray A.UArray Elt)

Although the release notes for 7.10 fail to mention it, I see https://ghc.haskell.org/trac/ghc/ticket/9220#comment:11 admits its a breaking change. But what's the solution – do I really have to create a newtype wrapper for MyArr with helper functions for every usage?


Solution

  • You don't have to create a wrapper for MyArr, but you will have to write out by hand the instance you would previously have derived. A bit of a brute force solution is to unsafeCoerce your way through the IArray instance manually (the reason you can't coerce is the same you can't derive).

    {-# LANGUAGE InstanceSigs, ScopedTypeVariables, MultiParamTypeClasses #-}
    
    import Data.Array.Base
    import Data.Array.IArray
    import Data.Array.Unboxed
    import Unsafe.Coerce
    
    instance IArray UArray Elt where
      bounds :: forall i. Ix i => UArray i Elt -> (i, i)
      bounds arr = bounds (unsafeCoerce arr :: UArray i Int)                                                                                                                                                                        
    
      numElements :: forall i. Ix i => UArray i Elt -> Int
      numElements arr = numElements (unsafeCoerce arr :: UArray i Int)
    
      unsafeArray :: forall i. Ix i => (i,i) -> [(Int, Elt)] -> UArray i Elt
      unsafeArray lu ies = unsafeCoerce (unsafeArray lu [ (i,e) | (i,Elt e) <- ies ] :: UArray i Int) :: UArray i Elt
    
      unsafeAt :: forall i. Ix i => UArray i Elt -> Int -> Elt
      unsafeAt arr ix = Elt (unsafeAt (unsafeCoerce arr :: UArray i Int) ix)