是否有一种优雅的方式让函数返回相同类型的函数(在元组中)

Lil*_*ily 7 haskell recursive-datastructures

我正在使用haskell来实现一个涉及返回值的函数的模式,以及它们自己(或相同类型的函数).现在我已经实现了这样:

newtype R a = R (a , a -> R a)

-- some toy functions to demonstrate    
alpha :: String -> R String
alpha str
    | str == reverse str = R (str , omega)
    | otherwise          = R (reverse str , alpha)

omega :: String -> R String
omega (s:t:r)
    | s == t    = R (s:t:r , alpha)
    | otherwise = R (s:s:t:r , omega)
Run Code Online (Sandbox Code Playgroud)

这些类型的函数的驱动力是一个称为级联的函数:

cascade :: (a -> R a) -> [a] -> [a]
cascade _ [] = []
cascade f (l:ls) = el : cascade g ls where
    R (el , g) = f l
Run Code Online (Sandbox Code Playgroud)

它接受种子函数和列表,并返回通过将种子函数应用于列表的第一个元素而创建的列表,将其返回的函数应用于列表的第二个元素,依此类推.

这是有效的 - 但是,在使用这个稍微更有用的东西的过程中,我注意到很多次我的基本单位是函数,它们很少返回除了它们之外的函数; 并明确声明一个函数返回自己变得有点单调乏味.我宁愿能够使用类似Monad return函数的东西,但是,我不知道如何bind对这些类型的函数做什么,特别是因为我从来没有打算将这些函数与除了它们首先返回的函数之外的任何东西相关联.

试图把这个变成莫纳德,开始让我担心我做的事情是否有用,所以,简而言之,我想知道的是:

  • 我在做什么坏事?如果不,
  • 有没有我在做什么之前/我在这里重新发明轮子?如果不,
  • 有没有一种优雅的方式来做到这一点,或者我已经达到了这个并且因为想要某种return模拟而贪婪?

(顺便说一下,除了'返回自己的功能'或'递归数据结构(功能)'之外,我不太清楚这种模式被称为什么,并且已经尝试在其中进行有效的研究 - 如果任何人都可以给我一个这个模式的名称(如果它确实有一个),单独这将是非常有帮助的)

Nik*_* B. 7

作为一个高级别的考虑,我会说你的类型代表一个有状态的流变换器.这里有点令人困惑的是你的类型被定义为

newtype R a = R (a , a -> R a)
Run Code Online (Sandbox Code Playgroud)

代替

newtype R a = R (a -> (R a, a))
Run Code Online (Sandbox Code Playgroud)

这在流媒体上下文中会更自然,因为如果你还没有收到任何东西,你通常不会"产生"某些东西.那么你的函数也会有更简单的类型:

alpha, omage :: R String
cascade :: R a -> [a] -> [a]
Run Code Online (Sandbox Code Playgroud)

如果我们试图概括一下流变换器的这个概念,我们很快就会意识到将as 列表转换为s列表a的情况只是一个特例.有了适当的基础设施,我们也可以生成一个bs 列表.所以我们尝试概括类型R:

newtype R a b = R (a -> (R a b, b))
Run Code Online (Sandbox Code Playgroud)

我已经看到这种结构被称为a Circuit,这恰好是一个完整的箭头.箭头是函数概念的概括,是比monad更强大的构造.我不能假装理解类别 - 理论背景,但与它们一起玩它绝对有趣.例如,简单的转换只是Cat.id:

import Control.Category
import Control.Arrow
import Prelude hiding ((.), id)
import qualified Data.List as L

-- ... Definition of Circuit and instances

cascade :: Circuit a b -> [a] -> [b]
cascade cir = snd . L.mapAccumL unCircuit cir

--
ghci> cascade (Cat.id) [1,2,3,4] 
[1,2,3,4]
Run Code Online (Sandbox Code Playgroud)

我们还可以通过参数化我们返回的电路来模拟状态作为延续:

countingCircuit :: (a -> b) -> Circuit a (Int, b)
countingCircuit f = cir 0
    where cir i = Circuit $ \x -> (cir (i+1), (i, f x))

--
ghci> cascade (countingCircuit (+5)) [10,3,2,11]
[(0,15),(1,8),(2,7),(3,16)]
Run Code Online (Sandbox Code Playgroud)

我们的电路类型是一个类别的事实为我们提供了一个很好的组合电路的方法:

ghci> cascade (countingCircuit (+5) . arr (*2)) [10,3,2,11]
[(0,25),(1,11),(2,9),(3,27)]
Run Code Online (Sandbox Code Playgroud)