2017-04-05 26 views
2

我明白,在Haskell中,State monad非常有用,因为没有可变变量(除非我们在IO monad中)。状态monad是否需要/有用可变(本地)变量(如Scala)的语言?

但是,斯卡拉的交易是什么? State Monad是否适用于有可变变量的语言?

从某种意义上说,所有State Monad允许的是use some local mutable variables within the Monad context。例如:

newtype Labeled anytype = Labeled (S -> (S, anytype)) 

instance Monad Labeled where 

    return contents = Labeled (\st -> (st, contents)) 

    Labeled fst0 >>= fany1 = 
    Labeled $ \st0 -> 
     let (st1, any1) = fst0 st0 
      Labeled fst1 = fany1 any1 
     in fst1 st1 

mlabel :: Tr anytype -> Lt anytype 
mlabel tr = let Labeled mt = mkm tr 
      in snd (mt 0) 

mkm :: Tr anytype -> Labeled (Lt anytype) 

mkm (Lf x) 
    = updateState >>= \n -> return $ Lf (n,x) 

mkm (Br l r) 
    = mkm l >>= \l' -> 
    mkm r >>= \r' -> 
    return $ (Br l' r') 

updateState :: Labeled S 
updateState = Labeled (\n -> ((n+1),n)) 

main = print $ mlabel tr1 

这段代码在使用Scala中的可变变量时会更简单(3-4行)。喜欢的东西: case class Tr (...)

case class LTr (...)

def labelTree = (... recursive call ...)

其中labelTree使用本地可变变量来存储标签的当前状态。

我真的没有看到State Monads在Scala中的用处。为什么会有人在斯卡拉使用State Monad?有没有好的用例呢?

我可以想象的唯一用例是,如果有状态计算是复杂的并且是由几个State Monads组成的,但是在那时我不确定使用State Monad与普通的旧函数式编程和显式参数传递(而不是隐含的参数传递,这是State Monad所在的)。

+3

这可能是一个意见问题。但是有些想法:Haskell可以做可变引用(IORef,STRef等),所以你在Scala中给出的不使用状态的任何原因对于Haskell来说都是一样的。那么为什么哈斯克勒使用国家? – user2297560

+0

我也猜测它在可变性语言中并不那么有用。也许可以使用状态monad来记录一个特定的子程序只改变几个变量的状态,但是保持状态的其余部分不变。也许这不值得付出努力,特别是如果状态monad被实现为一个函数,因此效率低于普通可变变量(至少没有优化)。 – chi

+2

@ user2297560也许他们不想在IO monad中出于某种原因? – jhegedus

回答

3

State monad对可变变量(即函数式编程思维模式)的巨大优势是参考透明度。

使用状态monad的代码块比使用var s的等效块更容易进行单元测试,更易于维护且不易出错,因为代码的逻辑不会取决于代码的运行位置和时间。

同一问题的另一个观点是,状态monad在类型系统中编码的值是可变的,而var不给出这样的信息。

编辑

但是,国家单子大概总是比可变的变量效率较低,因为它增加了编写的代码和编译后的输出之间的抽象的另一个层次。但是对于大多数函数式结构来说,这是可以说的,所以这对于在功能性的Scala代码中使用var并不是一个真正的参数。

+1

正确,但如果'vars'在本地使用**(在State Monad中)?在一个函数里面?像上面的例子一样?您可以像注入状态单元一样注入状态并从函数中提取状态(使用局部变量),因此它与本地解决方案一样可测试/可维护。隐藏状态的功能与State Monad一样透明,因为您无法传递“半评估”功能。 – jhegedus

+0

这就成为一个更有见地的问题,很像这个问题http://stackoverflow.com/questions/42995875/where-should-one-draw-the-line-between-functional-programming-and-imperative-pro –

+1

关于“总是效率较低”,我不确定。我认为可以使用可变性来实现Scala State monad,从而恢复大部分性能。 – chi

5

在Haskell中编写有状态动作时,有一个习惯用法,其中动作只需要知道完成其任务所需的最低状态,然后将每个动作zoomed转换为组合“全局状态”。例如:

import Control.Lens 
import Control.Monad.Trans.State 

succInt :: StateT Int IO String 
succInt = do 
    modify succ 
    return "Incr an Int!" 

succChar :: StateT Char IO String 
succChar = do 
    modify succ 
    return "Incr a Char!" 

type GlobalState = (Char,[Int]) 

composite :: StateT GlobalState IO (String,String) 
composite = do 
    r1 <- zoom _1 succChar 
    r2 <- zoom (_2.traversed) succInt 
    return (r1,r2) 

如果我们执行的例子是这样的:

ghci> runStateT composite ('a',[1,5,7]) 

结果是

(("Incr a Char!","Incr an Int!Incr an Int!Incr an Int!"),('b',[2,6,8])) 

我认为国家的“放大”到更大的国家会更很难用可变变量来完成。

+0

也就是说,“monad-unlift-ref”包在Haskell中提供了一个基于引用的状态monad http://hackage.haskell.org/package/monad- unlift-ref – danidiaz

+0

感谢您的回答,这是很好的东西。 – jhegedus

相关问题