简体   繁体   English

通过部分单调函数在ADT上创建折叠/同构

[英]Creating a fold / catamorphism over ADT with partial monadic functions

I'm a bit unsure as what the actual name is for what I am trying, creating a fold or catamorphism seems the be the name for it. 我有点不确定,因为我要尝试使用的实际名称是什么,创建折叠或变形效果似乎就是它的名称。

I've got the following data structures with a fold: 我有以下折叠的数据结构:

type Order = [Int]
data Orders = Orders FilePath Order Order

type FoldOrders m o os = FilePath -> o -> o -> m os
type FoldOrder m o i = [i] -> m o
type FoldInt m i = Int -> m i

type Fold m os o i = (FoldOrders m o os, FoldOrder m o i, FoldInt m i)

foldOrders :: (Monad m) => Fold m os o i -> Orders -> m os
foldOrders (fos, fo, fi) (Orders orders1 orders2) = do o1 <- foldOrder orders1
                                                       o2 <- foldOrder orders2
                                                       fos o1 o2
    where foldOrder order = do o <- mapM foldInt order
                               fo o
          foldInt int     = fi int

This fold works fine with for example this 'implementation': 此折叠可以很好地与以下“实现”配合使用:

simpleWrite :: Fold IO () () ()
simpleWrite = (fos, fo, fi)
where fos _ _ = return ()
      fo  _   = return ()
      fi  i   = putStrLn $ show i

Using this command 使用此命令

foldOrders simpleWrite (Orders [1,2] [3,4])

it prints out 1 2 3 4 like you would expect. 它会像您期望的那样打印1 2 3 4

So far so good, but.. 到目前为止还不错,但是..

When I want to 'push' down some information (a filepath in this case) while walking over the datastructure like so: 当我想像这样遍历数据结构时“压入”某些信息(在这种情况下为文件路径):

write :: Fold IO a b c
write = (fos, fo, fi)
     where fos path fo1 fo2 = do _ <- fo1 path
                                 _ <- fo2 path
                                 return ()
           fo fis path = do ios <- mapM (\x -> x path) fis
                            return ()           
           fi int path = appendFile path $ show int

I can't get it to compile. 我无法编译它。 It gives back this error: 它返回此错误:

Couldn't match type `FilePath -> IO ()' with `IO c'
Expected type: FoldInt IO c
  Actual type: Int -> FilePath -> IO ()
In the expression: fi

It seems like you cannot return a partial monadic function like this, but why is that? 看来您无法返回这样的部分Monadic函数,但是为什么呢? And how can I get this to work? 我该如何工作呢?

If you want to write to some specific file, then that file should just be a parameter to the write function, ie write :: FilePath -> Fold IO abc . 如果要写入某个特定文件,则该文件应该只是write函数的参数,即write :: FilePath -> Fold IO abc But as I understand, you want to compute the file path from the actual data. 但是据我了解,您想根据实际数据计算文件路径。 In this case, the filepath depends on the size of the data, so this is what you need to compute. 在这种情况下,文件路径取决于数据的大小 ,因此这是您需要计算的。 You also need to compute a continuation of type FilePath -> IO () - you have the latter, but you are missing the former. 您还需要计算FilePath -> IO ()类型的延续 FilePath -> IO () -您拥有后者,但是缺少前者。

write :: Fold IO () (Int, FilePath -> IO ()) (FilePath -> IO ())
write = (fos, fo, fi) where 

 fos :: (Int, FilePath -> IO ()) -> (Int, FilePath -> IO ()) -> IO () 
 fos (sz1, fo1) (sz2, fo2) = do 
   let fp = show (sz1 + sz2) ++ ".txt" 
   sequence_ [fo1 fp, fo2 fp] 

 fo :: [ FilePath -> IO () ] -> IO (Int, FilePath -> IO ())
 fo fis = return (length fis, \fp -> mapM_ ($ fp) fis)

 fi :: Int -> IO (FilePath -> IO ())
 fi int = return $ \fp -> appendFile fp (show int)

As you can see, the principle is quite simple. 如您所见,原理很简单。 If you need to compute two things at once, just compute two things at once! 如果您需要一次计算两件事,只需一次计算两件事! Sizes are simply added while functions of type FilePath -> IO () are simply lifted pointwise and then sequenced. 只需添加大小,而将FilePath -> IO ()类型的函数简单地逐点提升,然后排序即可。

And a test (in ghci): 和测试(ghci):

>:! cat 4.txt
cat: 4.txt: No such file or directory
>foldOrders write  (Orders [1,2] [3,4])
>:! cat 4.txt
1234>foldOrders write  (Orders [1,2] [3,4])
>:! cat 4.txt
12341234>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM