我想写一个类型
函数的可怕的非参数版本pretty :: (Show a) => a -> Text
这样
pretty :: Text -> Text = id
pretty :: String -> Text = T.pack
pretty :: (Show a) => a -> Text = T.pack . show
因此,我们的想法是,任何已经拥有Show
实例的内容都可以变成一个非常好的" Text
仅show
Text
,除了我们想要特殊情况的String
和{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, FlexibleContexts #-}
{-# LANGUAGE DataKinds, ConstraintKinds #-}
module Pretty (pretty) where
import Data.Text (Text)
import qualified Data.Text as T
type family StringLike a :: Bool where
StringLike String = True
StringLike Text = True
StringLike a = False
class (b ~ StringLike a) => Pretty' a b where
pretty' :: a -> Text
instance Pretty' String True where
pretty' = T.pack
instance Pretty' Text True where
pretty' = id
instance (Show a, StringLike a ~ False) => Pretty' a False where
pretty' = T.pack . show
type Pretty a = (Pretty' a (StringLike a))
pretty :: (Pretty a) => a -> Text
pretty = pretty'
。
以下代码有效:
pretty
并且可以在不导出除pretty
函数之外的任何内容的情况下使用它。
但是,我对pretty :: (Pretty a) => a -> Text
的类型签名不太满意:
StringLike
我觉得由于(Show a)
是一个封闭类型的家庭,因此GHC应该有办法确定只有(Pretty a)
成立,1. The following hold trivially just by substituting the results of applying StringLike:
(StringLike String ~ True, Pretty' String True)
(StringLike Text ~ True, Pretty' Text True)
2. For everything else, we *also* know the result of applying StringLike:
(Show a, StringLike a ~ False) => (Pretty' a (StringLike a))
已经满足,因为:< / p>
{{1}}
有没有办法让GHC相信这个?
答案 0 :(得分:5)
我觉得由于
StringLike
是一个封闭式家庭,因此GHC应该有办法确定只有(Show a)
成立,(Pretty a)
已经满足
要做到这一点需要进行类型检查,并且会破坏参数多态性。考虑定义一个类型族
type family IsInt a :: Bool where
IsInt Int = True
IsInt a = False
class (b ~ IsInt a) => TestInt a b where
isInt :: a -> Bool
instance TestInt Int True where
isInt _ = True
instance (IsInt a ~ False) => TestInt a False where
isInt _ = False
现在通过你的论证,GHC应该能够满足TestInt a
的{{1}}。换句话说,我们应该能够测试任何给定类型是否等于Int。这显然是不可能的。
同样,()
字典等同于函数Show a
。如果只是这样,你怎么能决定论证是a -> ShowS
?
答案 1 :(得分:2)
也许我误解了你的目标,但这似乎需要很多工作来获得你想要的类型。
{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, UndecidableInstances, IncoherentInstances #-}
module Prettied where
import Data.Text (Text, pack)
class Pretty a where pretty :: a -> Text
instance Pretty Text where pretty = id
instance Pretty String where pretty = pack
instance Show a => Pretty a where pretty = pack . show
虽然pretty
似乎应该有Pretty a => a -> Text
类型,但由于IncoherentInstances
,它实际上会有Show a => a -> Text
类型。这可能应该在它自己的模块中,因为启用IncoherentInstances
是可能破坏有效代码的事情之一。