Haskell - cannot construct the infinite type

user1285245

I am following the Manning Haskell book to compose functions of robots fighting with lamdas:

-- robot has 3 properties: name/attack/hp
robot (name,attack,hp)  = \message -> message (name,attack,hp)

-- getters
name (n,_,_) = n
attack (_,a,_) = a
hp (_,_,hp) = hp
getName aRobot = aRobot name
getAttack aRobot = aRobot attack
getHP aRobot = aRobot hp

-- setters
setName aRobot newName = aRobot (\(n,a,h) -> robot (newName,a,h))
setAttack aRobot newAttack = aRobot (\(n,a,h) -> robot (n,newAttack,h))
setHP aRobot newHP = aRobot (\(n,a,h) -> robot (n,a,newHP))

printRobot aRobot = aRobot (\(n,a,h) -> n ++ " attack:" ++ (show a) ++ " hp:"++ (show h))
fight aRobot1 aRobot2 = setHP aRobot2 (getHP aRobot2 - getAttack aRobot1)

The fight funciton returns a copy of aRobot2(the defender) with deducted HP. Now load the code in GHCi and get this:

*Main> robot1 = robot ("aaa", 20, 100)
*Main> robot2 = robot ("bbb", 15, 120)
*Main> robot2AfterAttack = fight robot1 robot2

<interactive>:36:34: error:
    • Occurs check: cannot construct the infinite type:
        c1 ~ (([Char], Integer, c1) -> t0) -> t0
      Expected type: (([Char], Integer,
                       (([Char], Integer, c1) -> t0) -> t0)
                      -> (([Char], Integer, c1) -> t0) -> t0)
                     -> c1
        Actual type: (([Char], Integer,
                       (([Char], Integer, c1) -> t0) -> t0)
                      -> c1)
                     -> c1
    • In the second argument of ‘fight’, namely ‘robot2’
      In the expression: fight robot1 robot2
      In an equation for ‘robot2AfterAttack’:
          robot2AfterAttack = fight robot1 robot2
    • Relevant bindings include
        robot2AfterAttack :: c1 (bound at <interactive>:36:1)

I cannot figure out what goes wrong here.

Dan Robertson

Let’s try to add type signatures and see if we can work out what is going on:

type RobotInfo = (String,Int,Int)
type Robot = forall a. (RobotInfo -> a) -> a
-- robot has 3 properties: name/attack/hp
robot :: RobotInfo -> Robot
robot (name,attack,hp)  = \message -> message (name,attack,hp)

-- getters
name :: RobotInfo -> String
name (n,_,_) = n
attack, hp :: RobotInfo -> Int
attack (_,a,_) = a
hp (_,_,hp) = hp
getName :: Robot -> String
getName aRobot = aRobot name
getAttack, getHP :: Robot -> Int
getAttack aRobot = aRobot attack
getHP aRobot = aRobot hp

-- setters
setName :: Robot -> String -> Robot
setName aRobot newName = aRobot (\(n,a,h) -> robot (newName,a,h))
setAttack, setHP :: Robot -> Int -> Robot
setAttack aRobot newAttack = aRobot (\(n,a,h) -> robot (n,newAttack,h))
setHP aRobot newHP = aRobot (\(n,a,h) -> robot (n,a,newHP))

printRobot :: Robot -> String
printRobot aRobot = aRobot (\(n,a,h) -> n ++ " attack:" ++ (show a) ++ " hp:"++ (show h))
fight :: Robot -> Robot -> Robot
fight aRobot1 aRobot2 = setHP aRobot2 (getHP aRobot2 - getAttack aRobot1)

So we look at the types and see a few things

  1. If we give types names then it can be easier to understand what functions are supposed to do
  2. The type Robot is very weird. Why is a robot a function that gives you the state of the robot and then returns whatever you return? Instead, why is a robot not just the state of the robot, which is almost exactly the same but a lot less stupid.
  3. If one looks at the type of fight it is unclear what this means. I have to read the function to determine that the result is the new state of the second Robot after being hit by the first.

What was your type error from?

Well without type signatures Haskell infers some types (which aren’t higher ranked):

robot :: (a,b,c) -> ((a,b,c) -> d) -> d
hp :: (a,b,c) -> c
getAttack :: ((a,b,c) -> b) -> b
getHP :: ((a,b,c) -> c) -> c
setHP :: ((a,b,c) -> ((a,b,g) -> h) -> h) -> g -> ((a,b,g) -> h) -> h

This type already looks crazy but note that Haskell has inferred that setHP does not take a general robot but rather a specialised robot that can only give you back a new sort-of-robot with whatever you give it. What about when it tries to work out the type of fight?

fight aRobot1 aRobot2 =
  setHP aRobot2 (getHP aRobot2 - getAttack aRobot1)
  • Well because of the call to getAttack, we infer that aRobot1 :: (x,y,z) -> y.
  • Because of getHP we get aRobot2 :: (a,b,c) -> c
  • Because of - we get that c~y (they are the same type) and that Num c. So we now have aRobot1 :: (x,c,z) -> c
  • Now we have the call to setHP and that suggests that Robot2 :: (p,q,r) -> ((p,q,c) -> h) -> h and we need to reconcile these types.
  • So we match up the first arguments: p~a, q~b, r~c. Now we need to unify the results: c from the use of getHP with (a,b,c) -> h.
  • So c is the same as (a,b,c) -> h is the same as (a,b,(a,b,c) -> h) -> h and so on.

I don’t have ghc to hand but I don’t really understand why you didn’t get a type error when you tried to define fight. (Does anyone else know?)

Anyway here is how I would recommend that you write your program (in a non bizarre weird way):

data Robot = Robot { name :: String, attack :: Int, hp :: Int }
-- no need to define getters because we get them for free and no setters because we don’t use them
robot (name,attack,hp) = Robot name attack hp

instance (Show Robot) where
  show (Robot n a h) = n ++ " attack:" ++ (show a) ++ " hp:"++ (show h)

-- fight r1 r2 returns r2 after being attacked by r1
fights robot1 robot2 = robot2 { hp = hp robot2 - attack robot1 }

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

Haskell: cannot construct the infinite type

Haskell foldl cannot construct the infinite type

Haskell Newbie - Occurs check: cannot construct the infinite type: a ~ [a]

haskell fibonacci - cannot construct the infinite type: a0 = [a0]

Why do I get this "cannot construct the infinite type: a ~ [a]" error in Haskell?

Cannot construct infinite type with functor

Recursion in conditional definition in Haskell gives error ( Occurs check: cannot construct the infinite type:)

Split Function - cannot construct the infinite type

Struggling to understand a "Cannot construct the infinite type" error

cannot construct the infinite type: e ~ [e]

cannot construct the infinite type when using foldl

"infinite type" error in haskell, cannot find whats wrong

Cannot construct inifnite type

Compile error: Occurs check: cannot construct the infinite type: a1 = [a1]

Error on defining Applicative's <*> with record accessors: cannot construct the infinite type: t ~ t -> t1

How can I deal with "Occurs check: cannot construct the infinite type: a0 = [a0]"?

Haskell cannot match type

infinite type error reversing a list in Haskell

Cannot find or construct a Read instance for type: Option[A]

Haskell match construct analogous to F# type-test pattern?

Haskell Polymorphic Recursion with Composed Maps causes Infinite Type Error

How does Haskell type-check infinite recursive values?

Construct a type

Java scoping construct cannot be annotated with type-use

SQL: "Cannot construct data type date" when comparing two dates

Doobie cannot find or construct a Read instance for type T

Haskell - Cannot understand this type mismatch error

Haskell interpreter cannot infer return type

infinite type error in haskell when trying to translate python function into haskell. Why?