【问题标题】:Simplest way to join functions of same meaning but different return value type连接相同含义但返回值类型不同的函数的最简单方法
【发布时间】:2013-01-04 08:02:02
【问题描述】:

我正在编写小型“hello world”类型的程序,它通过不同的“原因”对相同的文件进行分组,例如相同的大小、相同的内容、相同的校验和等。

所以,我已经到了要编写这样一个函数的地步(DuplicateReason 是一种代数类型,它说明了两个文件相同的原因):

getDuplicatesByMethods :: (Eq a) => [((FilePath -> a), DuplicateReason)] -> IO [DuplicateGroup]

在每个元组中,第一个函数将是通过文件路径返回某些 (Eq a) 值的函数,例如字节串(带有内容),或带有校验和的 Word32,或带有大小的 Int。

显然,Haskell 不喜欢这些函数的类型不同,所以我需要以某种方式收集它们。

我看到它创建类型的唯一方法

data GroupableValue = GroupString String | GroupInt Int | GroupWord32 Word32

然后让生活更轻松,使 typeclass 像

class GroupableValueClass a where
  toGroupableValue :: a -> GroupableValue
  fromGroupableValue :: GroupableValue -> a

并为我要获得的每个值实现实例。

问题:我做得对吗?(如果没有)有没有更简单的方法来解决这个任务?

更新:

这是描述我想要什么的完整最小代码(简化,没有 IO 等):

data DuplicateGroup = DuplicateGroup

-- method for "same size" -- returns size
m1 :: String -> Int
m1 content = 10

-- method for "same content" -- returns content
m2 :: String -> String
m2 content = "sample content"

groupByMethods :: (Eq a) => [(String -> a)] -> [DuplicateGroup]
groupByMethods predicates = undefined

main :: IO ()
main = do
  let groups = (groupByMethods [m1, m2])
  return ()

【问题讨论】:

  • 我不确定您所说的Haskell doesn't like that these functions are of different types 是什么意思。你能引用你得到的错误吗?

标签: haskell


【解决方案1】:

列表总是同质的,因此您不能将具有不同 a 的项目放入同一个列表中(如您所见)。有几种方法可以解决这个问题,但我通常更喜欢使用GADTs。例如:

{-# LANGUAGE GADTs #-}

import Data.ByteString (ByteString)
import Data.Word

data DuplicateReason = Size | Checksum | Content
data DuplicateGroup

data DuplicateTest where
    DuplicateTest :: Eq a => (FilePath -> IO a) -> DuplicateReason -> DuplicateTest

getSize :: FilePath -> IO Integer
getSize = undefined

getChecksum :: FilePath -> IO Word32
getChecksum = undefined

getContent :: FilePath -> IO ByteString
getContent = undefined

getDuplicatesByMethods :: [DuplicateTest] -> IO [DuplicateGroup]
getDuplicatesByMethods = undefined

这个解决方案仍然需要一个新类型,但至少您不必提前指定所有情况或创建样板类型类。现在,由于泛型类型 a 本质上是“隐藏”在 GADT 中,因此您可以定义一个列表,其中包含具有不同返回类型的函数,并包装在 DuplicateTest GADT 中。

getDuplicatesByMethods
    [ DuplicateTest getSize Size
    , DuplicateTest getChecksum Checksum
    , DuplicateTest getContent Content
    ]

您也可以在不使用任何语言扩展或引入新类型的情况下解决这个问题,只需重新思考您的函数即可。主要目的是根据a的某些属性对文件进行分组,因此我们可以将getDuplicatesByMethods定义为

getDuplicatesByMethods :: [([FilePath] -> IO [[FilePath]], DuplicateReason)] -> IO [DuplicateGroup]

即我们采用一个根据某些标准对文件进行分组的函数。然后我们可以定义一个辅助函数

groupWith :: Eq a => (FilePath -> IO a) -> [FilePath] -> IO [[FilePath]]

然后像这样打电话给getDuplicatesByMethods

getDuplicatesByMethods
    [ (groupWith getSize, Size)
    , (groupWith getChecksum, Checksum)
    , (groupWith getContent, Content)
    ]

【讨论】:

  • 我想只是为了练习,我将从创建单独的类型(以及围绕它的类型类/助手)开始,然后查看 GADT。还是谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-12-18
  • 1970-01-01
  • 2019-05-09
  • 1970-01-01
  • 1970-01-01
  • 2015-02-05
  • 2019-04-15
相关资源
最近更新 更多