haskell - 我们如何在保持类型安全的同时通用地处理关联类型

标签 haskell types functional-programming functional-dependencies associated-types

这是我的问题:

假设我要处理各种类型的对象,但它们共享相同的形式:我们有由字符串(例如 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/

相关文章:

c# - 从类型名称获取可空类型

haskell - 以纯函数式语言保持状态

haskell - Netwire 5 中的 Kleisli Arrow?

Haskell 无法统一类型​​实例方程

haskell - 使用 OptParse-Applicative 将用户选项解析为自定义数据类型

MySQL数据类型: Text,,,错误:数据太长

c++ - 为什么 int 不能用作返回类型的左值,而用户定义的类可以?

javascript - 如果满足条件,Lodash 交换相邻的项目

haskell - Haskell类型错误编译

haskell - 在 Haskell 中向其他线程抛出异常的操作语义