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.
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
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. 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)
getAttack
, we infer that aRobot1 :: (x,y,z) -> y
. getHP
we get aRobot2 :: (a,b,c) -> c
-
we get that c~y
(they are the same type) and that Num c
. So we now have aRobot1 :: (x,c,z) -> c
setHP
and that suggests that Robot2 :: (p,q,r) -> ((p,q,c) -> h) -> h
and we need to reconcile these types. p~a
, q~b
, r~c
. Now we need to unify the results: c
from the use of getHP
with (a,b,c) -> h
. 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 }
This example is taken from Lesson 10 (Unit 1) of Will Kurt's “Get Programming With Haskell”. The task there is to fake the object-oriented programming languages model of “sending messages” to objects with a kind of CPS'ed approach: an object is a function which receives continuation and feeds it with the values of the object's fields:
type ObjectProperties = (Prop1, Prop2, ...)
object_constructor objprops = \message -> message objprops
As it is just first unit of the book, no type features besides functons and tuples are used. So, solving the puzzle with a data type is a bit of cheating.
The code provided by OP is almost just to the source with one exception: the implementation of fight
function. In the book it looks like this:
damage aRobot attackDamage = aRobot (\(n,a,h) ->
robot (n,a,h-attackDamage))
fight aRobot defender = damage defender attack
where attack = if (getHP aRobot) > 10
then getAttack aRobot
else 0
(The full source code of the example can be downloaded from the book's webpage .)
The example typechecks and works as expected. I agree that it is a bit uncomfortable to read this without the type annotations, but I think that the idea should be clear.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.