我定义了一个简单的代数(具体)数据类型,MyType:
data MyTpe = MyBool Bool | MyInt Int
Run Code Online (Sandbox Code Playgroud)
...我试图找到一种方法将"转换"任意函数(a-> b),其中a和b是Bool或Int,进入相关的(MyType-> MyType)函数.
这样做,它将(a-> b)转换为Maybe(MyType-> MyType)(参见下面的[1]):
import Data.Typeable
data MyTpe = MyBool Bool | MyInt Int deriving Show
liftMyType :: (Typeable a, Typeable b) => (a -> b) -> Maybe (MyTpe -> MyTpe)
liftMyType f = case castIntInt f of
Just g -> Just $ liftIntInt g
Nothing ->
case castIntBool f of
Just g -> Just $ liftIntBool g
Nothing ->
case castBoolInt f of
Just g -> Just $ liftBoolInt g
Nothing ->
case castBoolBool f of
Just g -> Just $ liftBoolBool g
Nothing -> Nothing
castIntInt :: (Typeable a, Typeable b) => (a -> b) -> Maybe (Int -> Int)
castIntInt f = cast f :: Maybe (Int -> Int)
castIntBool :: (Typeable a, Typeable b) => (a -> b) -> Maybe (Int -> Bool)
castIntBool f = cast f :: Maybe (Int -> Bool)
castBoolInt :: (Typeable a, Typeable b) => (a -> b) -> Maybe (Bool -> Int)
castBoolInt f = cast f :: Maybe (Bool -> Int)
castBoolBool :: (Typeable a, Typeable b) => (a -> b) -> Maybe (Bool -> Bool)
castBoolBool f = cast f :: Maybe (Bool -> Bool)
liftIntInt :: (Int -> Int) -> (MyTpe -> MyTpe)
liftIntInt f (MyInt x) = MyInt (f x)
liftIntBool :: (Int -> Bool) -> (MyTpe -> MyTpe)
liftIntBool f (MyInt x) = MyBool (f x)
liftBoolInt :: (Bool -> Int) -> (MyTpe -> MyTpe)
liftBoolInt f (MyBool x) = MyInt (f x)
liftBoolBool :: (Bool -> Bool) -> (MyTpe -> MyTpe)
liftBoolBool f (MyBool x) = MyBool (f x)
Run Code Online (Sandbox Code Playgroud)
然而,这非常难看并且不能很好地扩展:如果我想以这种方式扩展MyType怎么办?
data MyTpe = MyBool Bool | MyInt Int | MyString String
Run Code Online (Sandbox Code Playgroud)
...或者如果我还想将(a1 - > a2 - > b)转换为关联的(MyType-> MyType-> MyType)函数,其中a1,a2和b是Bool或Int?
我的问题:是否有一种简单,更优雅,更像Haskell的方式来处理这个问题?
[1]:未在所有 MyType元素上定义liftIntInt函数等(例如,没有为(MyBool x)元素定义liftIntInt).代码只是一个简化的案例,在现实生活中我正确地处理了这个问题.
你正在寻找一种类型
goal :: (a -> b) -> (MyType -> MyType)
Run Code Online (Sandbox Code Playgroud)
对于一些"合适"的选择a和b.这些"合适的"选择是静态已知的,因为静态已知定义MyType.
您正在寻找的是一个类型类.特别是,我们想要的是MultiParamTypeClassespragma
{-# LANGUAGE MultiParamTypeClasses #-}
class MapMyType a b where
liftMyType :: (a -> b) -> (MyType -> MyType)
Run Code Online (Sandbox Code Playgroud)
所以现在完整的类型liftMyType是
liftMyType :: MapMyType a b => (a -> b) -> (MyType -> MyType)
Run Code Online (Sandbox Code Playgroud)
我们可以使用类型类机械的各种实例存储liftMyType有它是可用的只有和什么时候a和b可以解决的是在那里种liftMyType有人居住.
instance MapMyType Int Int where liftMyType f (MyInt x) = MyInt (f x)
instance MapMyType Int Bool where liftMyType f (MyInt x) = MyBool (f x)
instance MapMyType Bool Int where liftMyType f (MyBool x) = MyInt (f x)
instance MapMyType Bool Bool where liftMyType f (MyBool x) = MyBool (f x)
-- (as a side note, this is a dangerous function to instantiate since it
-- has incomplete pattern matching on its `MyType` typed argument)
Run Code Online (Sandbox Code Playgroud)
现在,值得一提的是,MultiParamTypeClasses经常使用像这样的损害推理.特别是,如果我们在寻找的代码片段liftMyType a b,我们必须能够推断出的类型a和b自己(从提示例如,没有帮助传递"向下"的呼叫liftMyType),否则我们会得到一个模糊的实例编译失败.实际上,这使得这个特别糟糕的是,如果要么 直接推断出a或者b不能直接推断,我们就会得到编译失败.
在许多情况下,您可能希望通过FunctionalDependencies允许更多推断来在两个参数之间"流动"并使模糊错误不太常见来控制此问题.
但在这种情况下,我认为它是一种代码味道.虽然上面的代码是有效的(注释注释)但它有一种脆弱的解决方案.
| 归档时间: |
|
| 查看次数: |
134 次 |
| 最近记录: |