ManfredP
ManfredP

Reputation: 1047

Change record values in Haskell

I am trying to write a Haskell module with the following code:

module RectangleMover where

data Rectangle = Rectangle { xCoordinate :: Int
                           , yCoordinate :: Int
                           , width :: Int
                           , height :: Int
                           } deriving (Show)

move :: Rectangle -> Int -> Int -> Rectangle
-- Edit 1
move rec x y =
    let rec' = { xCoordinate + x
               , yCoordinate + y
               }
    return rec

To create a rectangle i would type:

let rec = Rectangle 10 10 20 30

But my question is now how to implement a function that "moves" this rectangle? In C# or Java the call would be something like this: rec.move(20,20); But how would this be written in Haskell?

This is unfortunately my first try with a functional programming language...

Edit 1: I added the code inside my function but still get a parse error at "xCoordinate + x" ...

Upvotes: 7

Views: 7122

Answers (3)

chepner
chepner

Reputation: 532238

You just add the offsets to the corner:

move :: Rectangle -> Int -> Int -> Rectangle
move (Rectangle x y w h) dx dy = Rectangle (x + dx) (y + dy) w h

With record syntax, though, you can write it as

move :: Rectangle -> Int -> Int -> Rectangle
move r@(Rectangle {xCoordinate = x, yCoordinate=y}) dx dy = r {
  xCoordinate = x + dx,
  yCoordinate = y + dy }

Longer, but only because of the long names chosen for the two fields to update. You might further define a Point type:

data Point = Point Int Int
movePoint :: Point -> Int -> Int -> Point
movePoint (Point x y) dx dy = Point (x + dx) (y + dy)

data Rectangle = Rectangle { corner :: Point, width :: Int, height :: Int }
move :: Rectangle -> Int -> Int -> Rectangle
move r@(Rectangle {corner=c}) dx dy = r { corner = movePoint c dx dy }

Upvotes: 11

Alec
Alec

Reputation: 32319

Given that this is your first time with Haskell, go with the record update answer already mentioned. However, for people googling this in the future, and for you too if you are feeling more ambitious (or for future learning), Haskell has this very popular and extremely powerful library called lens.

Here's how you can engineer a solution to your problem using it.

{-# LANGUAGE TemplateHaskell #-}
import Control.Lens

data Rectangle = Rectangle { _xCoordinate :: Int
                           , _yCoordinate :: Int
                           , _width :: Int
                           , _height :: Int
                           } deriving (Show)
makeLenses ''Rectangle

move :: Rectangle -> Int -> Int -> Rectangle
move rect dx dy= rect
                   & xCoordinate +~ dx
                   & yCoordinate +~ dy

This solution may not seem more powerful initially, but when you start trying to update nested records, I assure you the advantage becomes clear.

Upvotes: 14

Juan Pablo Santos
Juan Pablo Santos

Reputation: 1220

Haskell values are immutable, so you want to return a new Rectangle with the desired properties modified. It's simple, use "Record Update Syntax" like this let myNewRec = rec { yCoordinate = 20, xCoordinate = 20 }

Upvotes: 6

Related Questions