Home > Blockchain >  unless, do, $ - what these do in the follwing code, being written in Haskell
unless, do, $ - what these do in the follwing code, being written in Haskell

Time:10-09

I am new to Haskell; just for a project I am supposed to understand the source code: I have been looking at the Udemy courses, "Learn you a Haskell", and others but still struggling at this point: The snippet of code is for reporting bugs.

I want to know what "Named Instruction" stands for, and what the snippet of code does:

maybeReportBug :: Named Instruction -> Int -> Checker Uninits UninitBug ()
maybeReportBug ni lineno = do
  s0 <- getState
  let allTainted = tainted s0
      allFields  = bitfields s0
  unless (null allTainted) $ do
    let names = catMaybes $ map nameOf $ getOperands ni

where:

blankUninit :: Uninits
blankUninit = Uninits S.empty M.empty S.empty

data Uninits = Uninits { uninits   :: S.Set Name
                       , tainted   :: M.Map Operand (S.Set Name)
                       , bitfields :: S.Set Name
                       }
             deriving (Eq, Ord, Show)

And where

getState :: Checker a b a
getState = curState `liftM` get

And where

newtype Checker a b c = Checker { unChecker :: StateT (CheckerState a b) IO c }
  deriving (Functor, Applicative, Monad, MonadState (CheckerState a b), MonadIO)

CodePudding user response:

Let me try to answer the question asked in the title - the code you are trying to understand is very complicated for a beginner, and Haskell is very much unlike any programming language that you have seen before.

$ - this is the precedence operator.

The parser in Haskell parses the code from left to right. So, if I want to print out a factorial:

print factorial 6

How does Haskell understand this? Haskell starts with print, and then looks for the arguments to print. Haskell reads factorial but doesn't know how to print one of those and emits an error message.

We should have put parentheses around factorial 6. That would work, but be a bit ugly.

print (factorial 6)

Instead we might use $ (not to be confused with <$> which is an abbreviation for fmap and very different). What $ does is to defer the left hand side until the right hand side is done. So we could say:

print $ factorial 6.

Now Haskell will wait until factorial 6 is done before trying to print it. And it looks better than a lot of parentheses.

do - monad sugaring

What you need to understand is that the code that Haskell is actually running is very different to the code that you type in.

If you have a list you can extract the contents using the >>= operator. This gives me raw data that I can process, for example multiplying the data by two, before turning it back into a list with return.

[1,2,3] >>= (\x -> return $ 2*x) 

This is workable code but ugly.

All of Haskell's code is like this, and you can tell Haskell to show you these things. However, most of the time we're quite happy to not see this. Hence, we sugar the code, that is to say we find an easier way of saying what we want to say, and let Haskell figure it out:

main = do
    putStr "What is your name: "
    name <- readLine
    putStrLn $ "Hello "    name

Which is equivalent to this de-sugared equivalent:

main = 
    putStr "What is  your name: " >>
    getLine >>= (\name ->
    putStrLn $ "Hello "    name)

The first one looks like Python, the second one is more confusing - and if the code goes on a bit, with branches, the de-sugared version gets harder and harder to write and maintain.

unless - make a decision

There are many ways of making a decision in Haskell, and two of them are when and unless.

Both take a Boolean value as the first parameter. The difference is that when does something when the value is True, and unless does something when the value is False. Unlike if .. then .. else, when and unless don't need both branches of the decision.

import Control.Monad

main = do 
    when True $ putStrLn "True"
    unless False $ putStrLn "False"
  • Related