Haskell 实现相同功能的不同类型的映射列表?
我想对列表(map)中的每个元素应用一个函数,但是这些元素可能有不同的类型,但都实现了相同的函数(这里是“putOut”),就像一个接口一样。但是,我无法创建此“接口”类型的列表(此处为“可输出”) 如何映射实现相同功能的不同类型的列表Haskell 实现相同功能的不同类型的映射列表?,haskell,interface,map,types,Haskell,Interface,Map,Types,我想对列表(map)中的每个元素应用一个函数,但是这些元素可能有不同的类型,但都实现了相同的函数(这里是“putOut”),就像一个接口一样。但是,我无法创建此“接口”类型的列表(此处为“可输出”) 如何映射实现相同功能的不同类型的列表 import Control.Monad main :: IO () main = do mapM_ putOut lst where lst :: [Outputable] -- ERROR: Class "Outputable" used as a
import Control.Monad
main :: IO ()
main = do
mapM_ putOut lst
where
lst :: [Outputable] -- ERROR: Class "Outputable" used as a type
lst = [(Out1 1),(Out2 1 2)]
class Outputable a where
putOut :: a -> IO ()
-- user defined:
data Out1 = Out1 Int deriving (Show)
data Out2 = Out2 Int Int deriving (Show)
instance Outputable Out1 where
putOut out1 = putStrLn $ show out1
instance Outputable Out2 where
putOut out2 = putStrLn $ show out2
Haskell不允许异构列表。因此,您不能列出输出文件,因为您的
Out1
和Out2
是两种不同的类型,即使它们都属于同一类型
但是有一种变通方法,允许使用存在量化
模拟异构列表。
请参阅Haskell wikibook中的示例
如何使用
{-#语言存在量化{-}
放在模块顶部 data ShowBox = forall s. Show s => SB s
heteroList :: [ShowBox]
heteroList = [SB (), SB 5, SB True]
instance Show ShowBox where
show (SB s) = show s
{-# LANGUAGE ExistentialQuantification #-}
main :: IO ()
main = do
mapM_ print lst
putStrLn "end"
where
lst :: [Printable]
lst = [P (Out1 1),P (Out2 1 2)]
-- box type (2)
data Printable = forall a . Show a => P a
-- necessary Show instance for the box type (3)
instance Show Printable where show (P x) = show x
-- user defined:
data Out1 = Out1 Int deriving (Show)
data Out2 = Out2 Int Int deriving (Show)
您确定要在列表中放置不同的类型吗
您可以使用类似于jetxee的存在量化示例,但请想想它实际起到了什么作用:您有一个未知类型的术语列表,您唯一能做的就是应用putOut
以获取IO()
值。也就是说,如果“接口”只提供一个具有已知结果类型的函数,则存在词列表和结果列表之间没有区别。前者的唯一可能用途是将其转换为后者,那么为什么要添加额外的中间步骤呢?改用类似的方式:
main :: IO ()
main = do
sequence_ lst
where lst :: [IO ()]
lst = [out1 1, out2 1 2]
out1 x = putStrLn $ unwords ["Out1", show x]
out2 x y = putStrLn $ unwords ["Out2", show x, show y]
一开始,这似乎违反直觉,因为它依赖于Haskell的一些不同寻常的特性。考虑:
- 不进行额外计算——延迟计算意味着
,show
,&c。除非执行unwords
操作,否则不会运行IO
- 简单地创建
值不涉及任何副作用——它们可以存储在列表中,也可以在纯代码中传递,等等。只有IO()
中的main
函数运行它们sequence\
Eq
这样需要两个类型的值的实例,它不起作用,但是一个存在主义列表也不会更好,因为您不知道任何两个值是否是相同的类型。在这种情况下,您所能做的就是检查每个元素是否与自身相等,然后您也可以(如上所述)创建一个Bool
s的列表并使用它
在更一般的情况下,最好记住,Haskell类型的类不是OOP接口。类型类是实现特殊多态性的强大手段,但不太适合隐藏实现细节。OOP语言倾向于通过将所有内容绑定到同一类层次结构,将即席多态性、代码重用、数据封装、行为子类型等融合在一起;在Haskell中,您可以(而且通常必须)单独处理每一个问题 粗略地说,OOP语言中的对象是一组(隐藏的、封装的)数据,这些数据与操作这些数据的函数捆绑在一起,每个函数都将封装的数据作为隐式参数(
this
,self
,等等)。要在Haskell中复制这一点,您根本不需要类型类:
- 将每个“类方法”作为常规函数编写,并显式设置
参数self
- 将每个函数部分应用于“封装”数据的值
- 将部分应用的函数合并为一个记录类型
Monad
,它们通常也是相同的类型类,不能用传统的OOP接口来表示,现代版本的C#大量使用了一元风格,但没有提供任何类型的通用IMonad
接口
请参见我所说的内容。您可能还想查看一个库的示例,该库提供可扩展、可组合的图形,而不使用类型类当用户无法使用此列表中的用户定义类型时,如何设计程序模块化?它们是:-)这就是存在量化的目的。我认为您应该了解数据类型和类型类之间的区别。例如,请参见此问题的答案>“是否确实要将不同类型放入列表?”<实际上,我有一个由模型(如圆形、矩形、网格等)和资源(纹理等)组成的渲染系统。在处理步骤I转换模型时,返回(不同)模型的列表,然后将其传递到输出IO,输出IO绘制与模型对应的资源。为了让用户使用自定义模型扩展库,我需要提供一些接口概念(至少这是我在OOP中要做的)。@sisif:有关如何在Haskell中表示OOP风格接口的更好描述,请参阅我的扩展答案。