gpt4 book ai didi

haskell - 有没有办法在 Haskell 中将多态函数应用于具有不同参数类型的多个对象?

转载 作者:行者123 更新时间:2023-12-01 13:18:05 26 4
gpt4 key购买 nike

这是我面临的问题的简化版本(因此在功能上它没有意义,但这是一个类型问题)。

module EGBase where
import Prelude

newtype SomeData a b = SomeData String

module EGChild where
import EGBase
import Prelude

myData :: SomeData Int Int
myData = SomeData "Child"

module EGChild1 where
import EGBase
import Prelude

myData :: SomeData Int String
myData = SomeData "Child 1"

module EGMain where
import EGBase
import EGChild
import EGChild1
import Prelude

worker1 :: SomeData a b -> IO ()
worker1 _ = putStrLn "Hello from Worker 1"

worker2 :: SomeData a b -> IO ()
worker2 _ = putStrLn "Hello from Worker 2"

mergeThem :: [IO ()] -> IO ()
mergeThem = foldl (>>) (pure ())

main1 :: IO ()
main1 = mergeThem [
worker1 EGChild.myData,
worker1 EGChild1.myData
]

main2 :: IO ()
main2 = mergeThem [
worker2 EGChild.myData,
worker2 EGChild1.myData
]

上面的编译但在实际应用程序中我可能有很多不同的工作人员我想应用于许多模块的列表(创建应用程序的许多版本)。我想抽象出来而不是硬编码辅助函数。

作为第一步,我尝试将一个硬编码的工作人员从列表中拉出来,但这没有用,因为列表项的类型不同:

-- different types in list [SomeData Int Int, SomeData Int String]
main2Dry :: IO ()
main2Dry = mergeThem $ worker2 <$> [
EGChild.myData,
EGChild1.myData
]

我真正想要的是这样的东西,但由于与上述相同的原因,这行不通:

mainShared :: (SomeData a b -> IO ()) -> IO ()
mainShared worker = mergeThem $ worker <$> [
EGChild.myData,
EGChild1.myData
]

以下也可以,但我想这里的问题是 worker 函数的具体类型是从列表中的第一个元素推断出来的,这意味着将 worker 应用于第二个元素时会出现问题:

mainShared :: (SomeData a b -> IO ()) -> IO ()
mainShared worker = mergeThem [
worker EGChild.myData,
worker EGChild1.myData
]
--
-- does not compile
--
-- src\EGMain.hs:50:28-42: error:
-- * Couldn't match type `b' with `String'
-- `b' is a rigid type variable bound by
-- the type signature for:
-- mainShared :: forall a b. (SomeData a b -> IO ()) -> IO ()
-- at src\EGMain.hs:47:1-46
-- Expected type: SomeData a b
-- Actual type: SomeData Int String
-- * In the first argument of `worker', namely `EGChild1.myData'
-- In the expression: worker EGChild1.myData
-- In the first argument of `mergeThem', namely
-- `[worker EGChild.myData, worker EGChild1.myData]'

那么我想在 Haskell 中实现的目标是可能的吗?

最佳答案

在标准的 Haskell 中是不可能的,但是有几个扩展可以让你做到这一点,包括 GADTs 和 RankNTypes。

一种可能适用于您的简化示例但可能不适用于您的真实代码的解决方法是仅删除虚拟类型参数:

erase :: SomeData a b -> SomeData () ()
erase (SomeData x) = SomeData x

mainShared :: (SomeData () () -> IO ()) -> IO ()
mainShared worker = mergeThem $ map worker [
erase EGChild.myData,
erase EGChild1.myData
]

关于haskell - 有没有办法在 Haskell 中将多态函数应用于具有不同参数类型的多个对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52713533/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com