封闭式家庭的限制?

时间:2014-04-15 03:00:56

标签: haskell type-families

我想写一个类型

函数的可怕的非参数版本
pretty :: (Show a) => a -> Text

这样

pretty :: Text -> Text = id
pretty :: String -> Text = T.pack
pretty :: (Show a) => a -> Text = T.pack . show

因此,我们的想法是,任何已经拥有Show实例的内容都可以变成一个非常好的" Textshow 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相信这个?

2 个答案:

答案 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是可能破坏有效代码的事情之一。