我试图write
在 Haskell 中实现一个 Pascal 风格的过程作为一个多变量函数。这是具有单态结果类型(IO
在这种情况下)的简化版本,可以正常工作:
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Main where
import Control.Monad.IO.Class
import Control.Monad.Trans.Reader
import System.IO
class WriteParams a where
writeParams :: IO () -> a
instance (a ~ ()) => WriteParams (IO a) where
writeParams = id
instance (Show a, WriteParams r) => WriteParams (a -> r) where
writeParams m a = writeParams (m >> putStr (show a ++ " "))
write :: WriteParams params => params
write = writeParams (return ())
test :: IO ()
test = do
write 123
write ('a', 'z') True
但是,当将结果类型更改为多态类型时,要在具有MonadIO
实例的不同 monad 中使用该函数,我遇到了重叠或无法确定的实例。具体来说,a ~ ()
以前版本的那个技巧不再起作用了。最好的方法是以下需要大量类型注释的方法:
class WriteParams' m a where
writeParams' :: m () -> a
instance (MonadIO m, m ~ m') => WriteParams' m (m' ()) where
writeParams' m = m
instance (MonadIO m, Show a, WriteParams' m r) => WriteParams' m (a -> r) where
writeParams' m a = writeParams' (m >> liftIO (putStr $ show a ++ " "))
write' :: forall m params . (MonadIO m, WriteParams' m params) => params
write' = writeParams' (return () :: m ())
test' :: IO ()
test' = do
write' 123 () :: IO ()
flip runReaderT () $ do
write' 45 ('a', 'z') :: ReaderT () IO ()
write' True
有没有办法让这个例子工作而不必在这里和那里添加类型注释并且仍然保持结果类型多态?