0

I am working on this program for better understanding of haskell and state monad. I made a module Rectangle which has data type Rectangle and a function to increment the length of the rectangle. To increment the length i made a function plus. Could there have been a better way than this plus function? Also while incrementing i am doing addition with a new rectangle which is logically incorrect. How to perform the increment in the correct way? Is there any way in which instead of giving the default width the compiler prompts the user for width? How can i sort of extract one or more rectangle or any attribute(length or breadth) while doing stateful computation?

{-# LANGUAGE TemplateHaskell, TypeOperators #-}
module Rectangle(Rectangle(Rectangle),GlobState(GlobState),plus,newGlobState,r1,r2,r3,incr) where

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

type Length = Int
type Width = Int

data Rectangle = Rectangle Length Width deriving (Eq,Read,Show)

data GlobState = GlobState { _r1 :: Rectangle , _r2 :: Rectangle , _r3 :: Rectangle } deriving Show
$(mkLabels [''GlobState])

plus :: Rectangle -> Rectangle -> Rectangle
plus (Rectangle x z) (Rectangle y w) = Rectangle (x+y) z

newGlobState:: GlobState
newGlobState = GlobState { _r1 = Rectangle 0 10, _r2 = Rectangle 0 10, _r3 = Rectangle 0 10}

incr :: (GlobState :-> Rectangle) -> State GlobState ()
incr x = modify x (plus (Rectangle 1 10))

I have made a new module for selecting one of the rectangles:

{-# LANGUAGE TemplateHaskell, TypeOperators #-}
module ChooseRect(ChooseRect(ChooseRect),makeChoice,select) where

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

type Choice = Int

data ChooseRect = ChooseRect Rectangle Rectangle deriving Show

makeChoice:: Rectangle-> Rectangle->ChooseRect
makeChoice p1 p2 = ChooseRect p1 p2

select:: ChooseRect -> Choice -> Rectangle
select (ChooseRect (Rectangle x z) (Rectangle y w)) choice = if (choice==1)
                then let selectedRectangle = Rectangle x z
                in selectedRectangle
                else let selectedRectangle = Rectangle y w
                in selectedRectangle

But when i made changes to main module i m getting error.

module Main where

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

main :: IO ()
main = do
    let x = flip execState newGlobState $ do
        incr r1
        incr r2
        incr r1
        incr r3
        incr r3
    let y=makeChoice r1 r2
    print y
    print x

The error message is

Couldn't match expected type `Rectangle'
            with actual type `Data.Label.Abstract.Lens
                                (~>0) GlobState Rectangle'
In the first argument of `makeChoice', namely `r1'
In the expression: makeChoice r1 r2
In an equation for `y': y = makeChoice r1 r2

Please explain the error and how to remove it

6
  • Is it intentional that your incr forces the width of the rectangle to be 10? (All your rectangles have width 10 for now, but if e.g. r3 started as Rectangle 0 5, would you really want incr to make it Rectangle 1 10?) Commented Feb 1, 2012 at 10:14
  • No.. i want it to be rectangle 1 5. Thats why i asked that how can i make user to input the width Commented Feb 1, 2012 at 10:17
  • So not only is the type of your plus logically incorrect, it hides a bug as well. Commented Feb 1, 2012 at 10:32
  • what is the bug that it hides? Commented Feb 1, 2012 at 10:38
  • incr forces the width of the rectangle to be 10. Commented Feb 1, 2012 at 10:41

1 Answer 1

3
  1. Better than your plus would be a lengthenBy :: Length -> Rectangle -> Rectangle.

    Oh, and you're using modify wrongly... you need to use the modify from Control.Monad.State and the modify from Data.Label.Pure.

    incr x = Control.Monad.State.modify $ Data.Label.Pure.modify x (lengthenBy 1)
    
  2. See the functions getLine :: IO String (for getting a line of input) and read :: String -> Int or reads :: String -> [(Int, String)] (for turning it from a String into an Int; the former is simpler to use but crashes if it can't parse the input) (and putStr :: String -> IO () or putStrLn :: String -> IO () for showing a prompt).

    n.b. Do your i/o in the outermost do-block in main, and pass the results as extra parameters into your stateful computation.

  3. Replace

    makeChoice r1 r2
    

    with

    makeChoice (get r1 x) (get r2 x)
    

    Look at the types:

    r1, r2 :: GlobState :-> Rectangle
    makeChoice :: Rectangle -> Rectangle -> ChooseRect
    

    So r1 and r2 are not of the right type to pass to makeChoice.

Sign up to request clarification or add additional context in comments.

3 Comments

But if i implement plus like that it will clash with increment's definition where plus is used
The idea is that you put lengthenBy 1 instead of plus (Rectangle 1 10).
Read the error messages. Understand them. If you cannot, ask a new top-level question, and include the error messages in your new question.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.