1

我正在编写小型“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 ()
4

1 回答 1

2

列表总是同质的,因此您不能将不同的项目放入同一个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 中,因此您可以定义一个列表,其中包含具有不同返回类型的函数,并包装在DuplicateTestGADT 中。

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)
    ]
于 2013-01-04T08:24:53.610 回答