Understanding Haskell Type From Old Exam

I am studying Haskell exam and I am a little stuck on this question: enter image description here

My first thought was to write something like this

paste :: Region -> Image a -> Image a -> Image a
paste (Image bol) img1 img2 = if bol
                              then -- do the pasting
                              else -- well do nothing

      

But I don't know how to do it. Can anyone point me in the right direction?

+1


source to share


2 answers


While this is unattractive when you're actually sitting on an exam, here's the "teach a man to bait fish" answer: start without defining any outcome at all, and let the compiler (should be GHC≥7.8) comment on it. Note that the stub is wrong: if Image

- is just type

-def, you don't need a value constructor Image

to match the pattern on it.

newtype Image a = Image (Position -> a)
    -- it should be `newtype` instead of `type`, else there
    -- won't be an explicit `Image` value constructor
type Position = (Float, Float)
type Region = Image Bool

paste :: Region -> Image a -> Image a -> Image a
paste (Image bol) img1 img2 = _

      

GHC will return with

/tmp/wtmpf-file6644.hs:8:31:
    Found hole ‘_’ with type: Image a
    Where: ‘a’ is a rigid type variable bound by
               the type signature for
                 paste :: Region -> Image a -> Image a -> Image a
    ...

      

Ok, this is not surprising: we already know that the result must be an image. Now Image

there is only one constructor for, which is Image

why we know for sure that we need to build such an image. Still don't know anything about internal implementation:

paste (Image bol) img1 img2 = Image _

      

gives

/tmp/wtmpf-file6644.hs:8:37:
    Found hole ‘_’ with type: Position -> a
    Where: ‘a’ is a rigid type variable bound by
               the type signature for
                 paste :: Region -> Image a -> Image a -> Image a
               at /tmp/wtmpf-file6644.hs:7:10

      

Aha, so in Image

hides an extra function argument, hidden! Let's also the pattern match this:

paste (Image bol) img1 img2 = Image $ \pos -> _

/tmp/wtmpf-file6644.hs:8:47:
    Found hole ‘_’ with type: a
    ...

      

Ok, a

completely opaque, so now we know for sure that we can't do anything without additional information. Fortunately, we have additional information in the form of arguments. How the GHC will tell you:

    ...
    Relevant bindings include
      pos :: Position (bound at /tmp/wtmpf-file6644.hs:8:40)
      img2 :: Image a (bound at /tmp/wtmpf-file6644.hs:8:24)
      img1 :: Image a (bound at /tmp/wtmpf-file6644.hs:8:19)
      bol :: Position -> Bool (bound at /tmp/wtmpf-file6644.hs:8:14)

      



We can't deal with it ... which is good, because it makes it obvious what needs to be done: we can feed pos

as an argument bol

. The result then has a type Bool

(as opposed to itself bol

), so this is now a good candidate for a switch if

.

paste (Image bol) img1 img2 = Image $ \pos -> if bol pos
         then _
         else _

      

...

Found hole ‘_’ with type: a
      ...

      

seen this before, so we need more information again. Take a look back at these arguments: Image a

can still be mapped to a drawing

paste (Image bol) (Image img1) (Image img2)
  = Image $ \pos -> if bol pos
         then _
         else _

      

Now he says:

    Relevant bindings include
      pos :: Position (bound at /tmp/wtmpf-file6644.hs:9:14)
      img2 :: Position -> a (bound at /tmp/wtmpf-file6644.hs:8:39)
      img1 :: Position -> a (bound at /tmp/wtmpf-file6644.hs:8:26)
      bol :: Position -> Bool (bound at /tmp/wtmpf-file6644.hs:8:14)

      

Aha, so img1

they img2

can bring value a

as needed, we just need to feed them first Position

. Well, we still have it pos

, so it's obvious what to do:

paste (Image bol) (Image img1) (Image img2)
  = Image $ \pos -> if bol pos
         then img1 pos
         else img2 pos

      

... or with two images swapping (think about it yourself). But there are really no other definitions you can write using just the given information (and all of that!), So this typed hole routine is a pretty reliable way to implement a function.

+8


source


Good. You got the screen coordinates, represented by the rectangle [0.1] × [0.1]. Area is a Boolean function over this rectangle that returns true if and only if the coordinate is in this area. Then you have two images, img1

and img2

, which are mappings from coordinates to pixels (or theoretically to another).

You want to return a new image that is equal img1

for the inside coordinates reg

or img2

for the outside coordinates reg

. Images are mappings from coordinates to pixels, so you are indeed returning a function.

Type Region -> Image a -> Image a -> Image a

, but remember that Image a

there is Position -> a

a Region

- Image Bool

, so it really is (Position -> Bool) -> (Position -> a) -> (Position -> a ) -> Position -> a

. We'll need this final parameter Position

before -> a

and provide it as an argument for the three parameters of the function.

Thus,



paste :: Region -> Image a -> Image a -> Image a
-- Is really: (Position->Bool) -> (Position->a) -> (Position->a) -> Position -> a
paste reg overlay bg coords | reg coords == True = overlay coords
                            | otherwise          = bg coords

      

Longer version with test case:

module Image where

type Image a = Position -> a
type Position = (Float, Float) -- [0,1]×[0,1]

data Color = RGB Int Int Int -- Pixels have values in [0,255]×[0,255]×[0,255].
  deriving Show
-- Not efficient for real-world use, but will do for now.

type Region = Image Bool
type ColorImage = Image Color

paste :: Region -> Image a -> Image a -> Image a
-- Is really: (Position->Bool) -> (Position->a) -> (Position->a) -> Position -> a
paste reg overlay bg coords | reg coords == True = overlay coords
                            | otherwise          = bg coords

allRed :: ColorImage
allRed _ = RGB 255 0 0

allWhite :: ColorImage
allWhite _ = RGB 255 255 255

unitCircle :: Region
unitCircle (x,y) = sqrt (x'*x' + y'*y') <= 0.5
  where x' = x - 0.5
        y' = y - 0.5

redCircleOnWhite :: ColorImage
redCircleOnWhite = paste unitCircle allRed allWhite

      

You can test redCircleOnWhite

in the REPL.

+1


source







All Articles