gpt4 book ai didi

haskell - 从 (a->b) 到 (MyType->MyType)

转载 作者:行者123 更新时间:2023-12-02 17:52:21 27 4
gpt4 key购买 nike

我定义了一个简单的代数(具体)数据类型,MyType:

data MyTpe = MyBool Bool | MyInt Int

...我正在尝试找到一种方法将任意函数(a->b)“转换”为关联的(MyType->MyType)函数,其中a和b是Bool或Int。

这完成了工作,它将 (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)

然而,这非常丑陋并且不能很好地扩展:如果我想以这种方式扩展 MyType 怎么办?

data MyTpe = MyBool Bool | MyInt Int | MyString String

... 或者,如果我还想将 (a1 -> a2 -> b)(其中 a1、a2 和 b 为 Bool 或 Int)转换为关联的 (MyType->MyType->MyType) 函数怎么办? ..

我的问题:有没有一种简单、更优雅、更像 Haskell 的方法来处理这个问题?

[1]:liftIntInt 函数等未在所有 MyType 元素上定义(例如,liftIntInt 未为(MyBool x) 元素定义)。该代码只是一个简化的案例示例,在现实生活中我正确地处理了这个问题。

最佳答案

您正在寻找一种类型

goal :: (a -> b) -> (MyType -> MyType)

对于 a 的一些“合适”选择和b 。这些“合适”的选择静态地称为 MyType 的定义。是静态已知的。

您正在寻找的是类型类。特别是,我们需要 MultiParamTypeClasses编译指示

{-# LANGUAGE MultiParamTypeClasses #-}

class MapMyType a b where
liftMyType :: (a -> b) -> (MyType -> MyType)

现在是 liftMyType 的完整类型是

liftMyType :: MapMyType a b => (a -> b) -> (MyType -> MyType)

我们可以使用类型类机制来存储 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)

现在,值得一提的是MultiParamTypeClasses这样使用时常常会损害推理。特别是,如果我们正在查看代码片段 liftMyType a b我们必须能够推断 a 的类型和b自己(例如,没有从调用“向下”传递到 liftMyType 的提示的帮助),否则我们将得到一个不明确的实例编译失败。实际上,这尤其糟糕的是,如果 任一 a 我们都会遇到编译失败。或b无法直接推断。

在许多情况下,您可能希望使用 FunctionalDependencies 来控制此问题允许在两个参数之间进行更多的“流动”推断,并减少歧义错误。

但在这种情况下,我认为这是代码异味。虽然上面的代码可以工作(需要注意注释),但它给人的感觉是一个脆弱的解决方案。

关于haskell - 从 (a->b) 到 (MyType->MyType),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27393968/

27 4 0