是否可以在不使用可变性的情况下表示可变性?

时间:2015-08-06 20:43:42

标签: f# functional-programming immutability monads

在状态monad(以及其他......之类)上阅读this excellent series之后,我尝试重现以下场景而不使用可变变量(改编自计算点击次数的简单UI):

let mutable count = 1

let increment () = count <- count + 1

我无法想出办法。

这可以在不使用可变变量的情况下在F#中处理吗?它是如何处理其他不允许不变的功能语言的呢?或者我问错了问题?

2 个答案:

答案 0 :(得分:3)

变量变量的功能替代方法是从函数返回更新的值。状态计算可以通过获取状态的当前值并返回包含结果和状态的更新值的对的函数来建模。您可以在F#中将此类型定义为

type State<'s, 'a> = S of ('s -> ('s * 'a))

它只包含一个有状态函数。然后,您可以定义一些用于操作有状态函数的函数:

let returnState x = S (fun s -> (s, x))
let runState (S(f)) s = f s
let putState s = S (fun _ -> (s, ()))
let getState<'s, 'a> = S (fun (s: 's) -> (s, s))

returnState创建一个有状态计算,忽略状态并返回给定值。 runState以给定的初始状态运行有状态计算。 putState是一个有状态计算,它获取当前状态,而putState更新当前状态并返回一个虚拟值。

然后,您可以将有状态计算与以下函数结合使用:

let bindState<'s, 'a, 'b> (S(sf): State<'s, 'a>) (f : 'a -> State<'s, 'b>) = S (fun s ->
        let (s', r) = sf s
        let (S(sf')) = f r
        sf' s')

这将采用有状态计算和函数来给出来自第一个的结果值的新计算。然后它构造一个复合计算,它将运行第一次计算,使用该值构造下一个计算,然后运行给定的第一次计算返回的中间状态。

这形成了一个monad,因此您可以创建一个计算表达式并使用它来使用更方便的语法进行有状态计算:

type StateBuilder() = 
    member this.Return(x) = returnState x
    member this.Bind(s, f) = bindState s f
    member this.ReturnFrom(s: State<_,_>) = s

let state = StateBuilder()

let modifyState f = state {
    let! s = getState
    let s' = f s
    return! (putState s')
}

modifyState使用函数a -> a来转换计算中的状态值。然后,您可以编写一个函数来在有状态计算中递增计数器的值:

let incState = modifyState ((+)1)

答案 1 :(得分:-1)

如果您没有使用可变变量,则可以使用ref变量。有了它,您的代码将如下所示

TID: [0] [AS] [2015-08-06 14:23:11,242]  INFO {org.wso2.carbon.core.services.util.CarbonAuthenticationUtil} -  'admin@carbon.super [-1234]' logged in at [2015-08-06 14:23:11,242+0200] {org.wso2.carbon.core.services.util.CarbonAuthenticationUtil}
TID: [0] [AS] [2015-08-06 14:24:09,835] ERROR {org.apache.catalina.core.ContainerBase} -  ContainerBase.addChild: start:  {org.apache.catalina.core.ContainerBase}
org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/demo-osb-test-0.0.1-SNAPSHOT/1]]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:154)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:633)
    at org.wso2.carbon.tomcat.internal.CarbonTomcat.addWebApp(CarbonTomcat.java:280)
    at org.wso2.carbon.tomcat.internal.CarbonTomcat.addWebApp(CarbonTomcat.java:177)
    at org.wso2.carbon.webapp.mgt.TomcatGenericWebappsDeployer.handleWebappDeployment(TomcatGenericWebappsDeployer.java:222)
    at org.wso2.carbon.webapp.mgt.TomcatGenericWebappsDeployer.handleWarWebappDeployment(TomcatGenericWebappsDeployer.java:174)
    at org.wso2.carbon.webapp.mgt.TomcatGenericWebappsDeployer.handleHotDeployment(TomcatGenericWebappsDeployer.java:141)
    at org.wso2.carbon.webapp.mgt.TomcatGenericWebappsDeployer.deploy(TomcatGenericWebappsDeployer.java:116)
    at org.wso2.carbon.webapp.mgt.AbstractWebappDeployer.deployThisWebApp(AbstractWebappDeployer.java:140)
    at org.wso2.carbon.webapp.mgt.AbstractWebappDeployer.deploy(AbstractWebappDeployer.java:90)
    at org.wso2.carbon.webapp.deployer.WebappDeployer.deploy(WebappDeployer.java:42)
    at org.apache.axis2.deployment.repository.util.DeploymentFileData.deploy(DeploymentFileData.java:136)
    at org.apache.axis2.deployment.DeploymentEngine.doDeploy(DeploymentEngine.java:807)
    at org.apache.axis2.deployment.repository.util.WSInfoList.update(WSInfoList.java:144)
    at org.apache.axis2.deployment.RepositoryListener.update(RepositoryListener.java:377)
    at org.apache.axis2.deployment.RepositoryListener.checkServices(RepositoryListener.java:254)
    at org.apache.axis2.deployment.RepositoryListener.startListener(RepositoryListener.java:371)
    at org.apache.axis2.deployment.scheduler.SchedulerTask.checkRepository(SchedulerTask.java:59)
    at org.apache.axis2.deployment.scheduler.SchedulerTask.run(SchedulerTask.java:67)
    at org.wso2.carbon.core.deployment.CarbonDeploymentSchedulerTask.runAxisDeployment(CarbonDeploymentSchedulerTask.java:79)
    at org.wso2.carbon.core.deployment.CarbonDeploymentSchedulerTask.run(CarbonDeploymentSchedulerTask.java:124)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
    at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:304)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:178)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:745)

Caused by: java.lang.NoSuchMethodError: javax.servlet.ServletContext.getVirtualServerName()Ljava/lang/String;
    at org.apache.tomcat.websocket.server.WsServerContainer.<init>(WsServerContainer.java:147)
    at org.apache.tomcat.websocket.server.WsSci.init(WsSci.java:131)
    at org.apache.tomcat.websocket.server.WsSci.onStartup(WsSci.java:47)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5274)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)

我并不是特别关注这两个,虽然我使用mutable比ref更频繁。找到这个stackoverflow link,这也很好地解读了两者之间的差异。