haskell-jp / questions #100 at 2022-03-19 00:41:38 +0900

GeneralizedNewtypeDeriving はLensの makeFields が作る HasFoo のような複数の型引数を受け取る型クラスに対して使えますか?

具体的には、スニペットに添付したような状況を想定しています。

上をコメントアウトして、下のコメントアウトを外すと以下のようなエラーになります。

    • Can't make a derived instance of
        ‘HasBase Search String’ with the newtype strategy:
        GeneralizedNewtypeDeriving cannot be used on non-newtypes
    • In the stand-alone deriving instance for ‘HasBase Search String’
We can even derive instances of multi-parameter classes, provided the newtype is the last class parameter.
newtype で作る型がマルチパラメータークラスの最後の引数じゃないとダメそう
https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/newtype_deriving.html
なるほど、そういうものなのですね…
がんばるならこんな感じですかね?
{-# LANGUAGE DefaultSignatures          #-}
{-# LANGUAGE DerivingStrategies         #-}
{-# LANGUAGE FlexibleInstances          #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses      #-}
{-# LANGUAGE UndecidableInstances       #-}

module Main where

main :: IO ()
main = do
  print $ base (Search $ Noun "a" "b") ""

data Noun
  = Noun
  { nounSurf :: String
  , nounBase :: String
  }
  deriving (Eq, Ord, Read, Show)

instance HasBase Noun String where
  base _ _ = 0

instance HasBaseFlipped String Noun

newtype Search
  = Search
  { searchNoun :: Noun
  }
  deriving stock (Eq, Ord, Read, Show)
  deriving newtype (HasBaseFlipped String)

class HasBase a b where
  base :: a -> b -> Int

class HasBaseFlipped a b where
  base' :: b -> a -> Int
  default base' :: HasBase b a => b -> a -> Int
  base' = base

instance {-# OVERLAPPABLE #-} HasBaseFlipped b a => HasBase a b where
  base = base'
そうなりそうですね…
そこまで頑張るならTHで実装しようかなぁと思えました
ありがとうございます