这是我的问题:
假设我要处理各种类型的对象,但它们共享相同的形式:我们有由字符串(例如 id)和内容组成的 Items
code> 可以是任何东西。
因此,分解的问题可以总结如下:
我希望能够从 content
生成一个 item
,并以通用方式将其与 id 关联起来,但我希望类型系统能够约束接口(interface),例如我知道我会
取回传递的内容
的Item
。
以下是使用FunctionalDependency的尝试示例:
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
main :: IO ()
main = do
putStrLn $ show $ handleContent TitiAssociation $ read "TitiContent"
putStrLn $ show $ handleContent TotoAssociation $ read "TotoContent"
-- Expected
-- "TitiItem"
-- "TotoItem"
-- definition of the domain
data TitiContent = TitiContent String deriving (Read, Show)
data TotoContent = TotoContent String deriving (Read, Show)
data TitiItem = TitiItem String TitiContent deriving (Read, Show)
data TotoItem = TotoItem String TotoContent deriving (Read, Show)
--
class (Read a, Show a) => Content a where
class (Read a, Show a) => Item a where
instance Content TitiContent where
instance Content TotoContent where
instance Item TitiItem where
instance Item TotoItem where
-- Association of types
class (Content contentType, Item itemType) => ItemContentAssociation association contentType itemType | association -> contentType, association -> itemType, itemType -> association where
-- tokens to identify the types which will be handled
data TitiAssociation = TitiAssociation
data TotoAssociation = TotoAssociation
instance ItemContentAssociation TitiAssociation TitiContent TitiItem where
instance ItemContentAssociation TotoAssociation TotoContent TotoItem where
-- generic function for handling
handleContent :: (ItemContentAssociation association contentType itemType) => association -> contentType -> itemType
handleContent TitiAssociation TitiContent = TitiItem
handleContent TotoAssociation TotoContent = TotoItem
但是编译器提示:
tmp.hs:41:15: error:
* Couldn't match expected type `ass' with actual type `TitiAss'
`ass' is a rigid type variable bound by
the type signature for:
handleContent :: forall ass contentType itemType.
ItemContentAss ass contentType itemType =>
ass -> contentType -> itemType
at tmp.hs:40:1-92
* In the pattern: TitiAss
In an equation for `handleContent':
handleContent TitiAss TitiContent = TitiItem
* Relevant bindings include
handleContent :: ass -> contentType -> itemType
(bound at tmp.hs:41:1)
我还尝试了各种类型的 TypeFamilies 扩展,但编译器总是提示(如果需要,可以提供更多示例,但旨在首先保持初始帖子的合理大小)。
我在函数式编程领域还是个新手,所以请毫不犹豫地提出新方法,因为我确信我忽略了问题的许多方面。
提前非常感谢, 干杯!
最佳答案
几乎可以肯定,在 MPTC/FunDeps 世界和 TF 世界中,正确的答案是使 handleContent
成为 ItemContentAssociation
的方法。既然您在评论中询问了这一点,那么具体来说就是类型族的情况。
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeFamilyDependencies #-}
main :: IO ()
main = do
putStrLn $ show $ handleContent TitiAssociation $ read "TitiContent \"titi\""
putStrLn $ show $ handleContent TotoAssociation $ read "TotoContent \"toto\""
-- Expected
-- "TitiItem"
-- "TotoItem"
-- definition of the domain
data TitiContent = TitiContent String deriving (Read, Show)
data TotoContent = TotoContent String deriving (Read, Show)
data TitiItem = TitiItem String TitiContent deriving (Read, Show)
data TotoItem = TotoItem String TotoContent deriving (Read, Show)
--
class (Read a, Show a) => Content a where
class (Read a, Show a) => Item a where
instance Content TitiContent where
instance Content TotoContent where
instance Item TitiItem where
instance Item TotoItem where
-- Association of types
class (Content (ContentType association), Item (ItemType association)) =>
ItemContentAssociation association
where
type ContentType association = content | content -> association
type ItemType association
handleContent :: association -> ContentType association -> ItemType association
-- tokens to identify the types which will be handled
data TitiAssociation = TitiAssociation
data TotoAssociation = TotoAssociation
instance ItemContentAssociation TitiAssociation where
type ContentType TitiAssociation = TitiContent
type ItemType TitiAssociation = TitiItem
handleContent TitiAssociation c@(TitiContent s) = TitiItem s {- what string should be used here? if s, why also have c? -} c
instance ItemContentAssociation TotoAssociation where
type ContentType TotoAssociation = TotoContent
type ItemType TotoAssociation = TotoItem
handleContent TotoAssociation c@(TotoContent s) = TotoItem s {- what string? -} c
也就是说,这里有些东西闻起来很不对劲。重复代码的数量让我怀疑您对于如何从您最喜欢的其他语言进行设置的想法是错误的。不过,如果不进一步了解这些定义的动机,就很难详细说明如何解决这个问题。
关于haskell - 我们如何在保持类型安全的同时通用地处理关联类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68747583/