我将在示例之前简要解释我的思想链,以便如果其中任何一个没有意义,我们也能够解决这个问题.
假设对于我的数据声明的每个类型构造函数(产品类型的总和),有一个参数具有给定类型类的实例.在我看来,这意味着我能够向GHC/Haskell解释如何获得该特定类型,以便我的类型最终表现得像该类型类的实例.
例如:
data Vector
-- The class type I talked about
class Transformable a where
setPosition' :: Vector -> a -> IO ()
setOrigin' :: Vector -> a -> IO ()
setAngle' :: Float -> a -> IO ()
-- ... this goes a long way
data TCircleShape
data TSquareShape
data TTriangleShape
data TConvexShape
-- Large sum type that defines different types of Shape
data Shape = Circle Float TCircleShape
| Square Float String TSquareShape
| Triangle Float TTriangleShape
| Convex [Vector] Float TConvexShape
-- ...
-- Almost all of the the Shape constructors have at least one
-- parameter that has an instance of the Transformable typeclass:
instance Transformable TCircleShape
instance Transformable TSquareShape
instance Transformable TTriangleShape
instance Transformable TConvexShape
-- What I would like to write then is:
runOnTransformable :: Transformable a => (a -> IO ()) -> Shape -> IO ()
runOnTransformable = undefined -- (???)
-- What I am doing right now is simply expanding Shape manually:
setShapePosition :: Vector -> Shape -> IO ()
setShapePosition v (Circle _ ptr) = setPosition' v ptr
setShapePosition v (Square _ _ ptr) = setPosition' v ptr
-- and so on...
setShapeAngle' :: Float -> Shape -> IO ()
setShapeAngle' f (Circle _ ptr) = setAngle' f ptr
setShapeAngle' f (Convex _ _ ptr) = setAngle' f ptr
-- and so on...
Run Code Online (Sandbox Code Playgroud)
我眼中有一种清晰的图案,我想以某种方式抽象出这种展开方式.
有人可能会尝试为数据类型本身设置一个实例:
instance Transformable Shape where
setPosition' v (Circle _ ptr) = setPosition' v ptr
-- [...]
setAngle' f (Convex _ _ ptr) = setAngle' f ptr
-- [...]
Run Code Online (Sandbox Code Playgroud)
缺点是我必须通过再次手动解包类型类来"重新实现"所有方法,除非它是在实例声明中.对?
回到我的问题:有没有办法通知Haskell如何从sum类型解包和处理类型类?
我对Lens很熟悉,而没有使用TemplateHaskell,但是,如果使用所述功能可能是一个可能的解决方案,那么就去吧.
您的runOnTransformable函数无法按指定进行写入,因为其类型签名是错误的.
runOnTransformable :: Transformable a => (a -> IO ()) -> Shape -> IO ()
Run Code Online (Sandbox Code Playgroud)
意味着对于任何a,其中主叫方的runOnTransformable选,他们可以为您提供一个功能采取的具体a,你会调用该函数与a正确的类型,你就可以从某种程度上你有Shape对象产生的.现在,这显然是不可能的,因为它们可能会传递一个类型的函数,TSquareShape -> IO ()但是一个没有TSquareShape它的Shape .更糟糕的是,GHC会担心某人可能会定义instance Transformable Integer where {...},并且你需要能够处理这种情况,即使你的Shape类型没有任何方法可以猜测Integer给这个函数什么.
你不想说你的函数适用于任何函数Transformable a => a,而是调用者的函数必须适用于任何函数Transformable a => a,以便它愿意接受你的Shape类型中存在的任何值.您将需要RankNTypes扩展名以使您能够编写正确的签名:
runOnTransformable :: (forall a. Transformable a => a -> IO ()) -> Shape -> IO ()
Run Code Online (Sandbox Code Playgroud)
遗憾的是,在您完成此操作后,我仍然不知道为所有各种构造函数实现此函数的自动方式Shape.我认为应该可以使用Generic或Data,或者模板Haskell或其他东西,但这超出了我的知识范围.希望我在这里写的内容足以让你朝着正确的方向前进.
| 归档时间: |
|
| 查看次数: |
203 次 |
| 最近记录: |