让我们说我想在Haskell中进行如下的ADT:
data Properties = Property String [String]
deriving (Show,Eq)
我想知道是否可以为第二个列表提供有界和枚举的属性?基本上,列表的第一个元素是minBound
,最后一个元素是maxBound
。我在努力,
data Properties a = Property String [a]
deriving (Show, Eq)
instance Bounded (Properties a) where
minBound a = head a
maxBound a = (head . reverse) a
但没有太多运气。
答案 0 :(得分:4)
唔不,你不能做你所要求的,但也许你会在这个其他巧妙的伎俩中找到灵感。
{-# language ScopedTypeVariables, FlexibleContexts, UndecidableInstances #-}
import Data.Reflection -- from the reflection package
import qualified Data.List.NonEmpty as NE
import Data.List.NonEmpty (NonEmpty (..))
import Data.Proxy
-- Just the plain string part
newtype Pstring p = P String deriving Eq
-- Those properties you're interested in. It will
-- only be possible to produce bounds if there's at
-- least one property, so NonEmpty makes more sense
-- than [].
type Props = NonEmpty String
-- This is just to make a Show instance that does
-- what you seem to want easier to write. It's not really
-- necessary.
data Properties = Property String [String] deriving Show
现在我们到达关键部分,我们使用反射来生成可依赖于运行时值的类实例。粗略地说,你可以想到
Reifies x t => ...
是
的班级版本\(x :: t) -> ...
因为它在类级别运行,所以可以使用它来参数化实例。由于Reifies x t
绑定类型变量 x
,而不是术语变量,因此您需要使用reflect
来实际获取值背部。如果碰巧有类型以p
结尾的值,那么您只需将reflect
应用于该值即可。否则,你总是可以加强Proxy :: Proxy p
来完成这项工作。
-- If some Props are "in the air" tied to the type p,
-- then we can show them along with the string.
instance Reifies p Props => Show (Pstring p) where
showsPrec k p@(P str) =
showsPrec k $ Property str (NE.toList $ reflect p)
-- If some Props are "in the air" tied to the type p,
-- then we can give Pstring p a Bounded instance.
instance Reifies p Props => Bounded (Pstring p) where
minBound = P $ NE.head (reflect (Proxy :: Proxy p))
maxBound = P $ NE.last (reflect (Proxy :: Proxy p))
现在我们需要一种方法来实际绑定可以传递给类型级lambda的类型。这是使用reify
函数完成的。所以,让我们把一些Prop
扔到空中然后让蝴蝶网让它们回来。
main :: IO ()
main = reify ("Hi" :| ["how", "are", "you"]) $
\(_ :: Proxy p) -> do
print (minBound :: Pstring p)
print (maxBound :: Pstring p)
./dfeuer@squirrel:~/src> ./WeirdBounded
Property "Hi" ["Hi","how","are","you"]
Property "you" ["Hi","how","are","you"]
您可以将reify x $ \(p :: Proxy p) -> ...
视为将p
类型绑定到值x
;然后,您可以通过约束事物来传递类型p
,使其具有涉及p
的类型。
如果您只做了几件事,那么所有这些机器都不仅仅是必要的。如果它变得很好,那就是当你用含有幻影类型的值进行大量操作时会带来额外的信息。在许多情况下,您可以避免reflect
的大多数显式应用程序和显式代理处理,因为类型推断只会为您完成所有操作。有关此技术的一个很好的示例,请参阅hyperloglog
包。 HyperLogLog
数据结构的配置信息在类型参数中携带;这保证了在编译时,只有相似配置的结构才能相互合并。