Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

State Monad ExampleProblem

Tags:

haskell

I am working on this problem and had previously asked related question. Implementation of State Monad To further refine my code i tried to implement it using only one increment function.

module StateExample where
import Control.Monad.State

data GlobState = GlobState { c1 :: Int, c2:: Int, c3:: Int} deriving (Show)

newGlobState:: GlobState
newGlobState = GlobState { c1=0,c2=0,c3=0 }

incr :: String-> State GlobState ()
incr x = do
     modify(\g -> g {x =x g + 1})

main:: IO()
main =  do
    let a1= flip execState newGlobState $ do
        incr c1
        incr c2
        incr c1
    print a

But here i am getting an error

`x' is not a (visible) constructor field name

How can i remove this error?

like image 806
Rog Matthews Avatar asked Jan 18 '23 15:01

Rog Matthews


1 Answers

You have hit a weakness in Haskell: records are not first class values! Indeed, it would be very nice to write as you have done, but it is not possible. However, you can use different libraries to achieve the desired effect. This is how it looks if you use fclabels:

{-# LANGUAGE TemplateHaskell, TypeOperators #-}
module StateExample where

import Control.Monad.State hiding (modify)
import Data.Label (mkLabels)
import Data.Label.Pure ((:->))
import Data.Label.PureM

data GlobState = GlobState { _c1 :: Int , _c2 :: Int , _c3 :: Int } deriving Show
$(mkLabels [''GlobState])

newGlobState:: GlobState
newGlobState = GlobState { _c1 = 0, _c2 = 0, _c3 = 0 }

incr :: (GlobState :-> Int) -> State GlobState ()
incr x = modify x (+1)

main :: IO ()
main = do
    let a = flip execState newGlobState $ do
        incr c1
        incr c2
        incr c1
    print a

There are some magic parts here. We define the GlobState with the same record names but prependend with an underscore. Then the function mkLabels uses TemplateHaskell to define "lenses" for every field in the record. These lenses will have the same name but without the underscore. The argument (GlobState :-> Int) to incr is such a lens, and we can use the modify function from Data.Label.PureM which updates records defined this way inside the state monad. We hide modify from Control.Monad.State, to avoid collision.

You can look at the other functions in the documentation for PureM for other functions usable with state monads, as gets and puts.

If you do not have fclabels installed, but you have the cabal executable from the cabal-install package (which you get if you install the Haskell Platform), you can install fclabels by simply running:

 cabal install fclabels

If this is the first time you run cabal, you first need to update the database:

 cabal update
like image 107
danr Avatar answered Jan 25 '23 20:01

danr