Showing posts with label IO. Show all posts
Showing posts with label IO. Show all posts

Thursday, February 14, 2013

HASKELL BINDING OPERATORS

HASKELL BINDING OPERATORS

REVISED: Wednesday, January 24, 2024




Haskell Binding Operators.

We will be using the following import:

Prelude> import Control.Monad
Prelude Control.Monad>

As shown below the join, (>>=), and return binding operators all produce the same results.

Prelude Control.Monad> :t join
join :: Monad m => m (m a) -> m a

Prelude Control.Monad>

Prelude Control.Monad> :t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b
Prelude Control.Monad>


Prelude Control.Monad> :t return
return :: Monad m => a -> m a
Prelude Control.Monad>

I. BINDING OPERATORS


There are no variables in Haskell. In Haskell you never assign a variable, instead you bind a name to a value. However, you will often see the term variable used in describing Haskell programs as a result of its common usage.

In a pure functional language, a function can only read what is supplied to it in its arguments and the only way it can have an effect on the world is through the values it returns. Non-functional "IO actions," which have side effects, are hidden from the functional world of Haskell by the IO monad.

IO actions are defined within the IO monadIO actions resemble functions. IO actions do nothing when they are defined, but IO actions perform some task when they are invoked.

Monads allow us to express sequential execution of actions. We define our sequence of operations by placing IO operations inside of a monad.

Three Monad Laws
1. Left identity: return x >>= f is the same as f x
2. Right identity: m >>= return is no different than just m
3. Associativity: (m >>= f) >>= g is just like doing m >>= (\x -> f x >>= g)

The “glue” combinator >>= function is pronounced "bind."

Technically each Monad in Haskell is not a type, but a "type constructor." monads are in the category theory branch of mathematics. IO is a monad, so IO actions can be combined using either the do notation or the >> chevron function which is pronounced then and the >>= bind function operations from the Monad class.

Any IO procedure has one more parameter compared to what you see in its type signature. This parameter is hidden inside the definition of the type alias IO.

Binding operators combine two IO actions, executing them sequentially. Bind f x is normally written as x  >>=  fx >>= f is the action that first performs the action x, and captures its result, passing it to f, which then computes a second action to be performed. That action is then carried out, and its result is the result of the overall computation.

The <- kleisli arrow symbol desugars to >>= bind; the =<< bind function is exactly the same as the >>= bind function except it takes its arguments in the opposite order.

Prelude>  import Control.Monad
Prelude>

Prelude> :type (=<<)
(=<<) :: Monad m => (a -> m b) -> m a -> m b
Prelude>

The >>= bind function binding operator implements sequential composition allowing us to use the result of the first action it gets passed as an additional parameter to the second one.

Prelude> :type (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b
Prelude>

The >>= bind operator takes as its input a monadic value also known as an action, unpacks a regular non-monadic value from it, the details of which are specific to the particular monad, and passes that value as the input to the monadic function, which produces a monadic value action as its final result.

There are two fundamental monadic operations, one named "bind" the >>= operator and one named return.

The bind (>>=) operator is a monadic apply operator. It can be used to define a monadic composition operator, which is written (>=>) and read as fish

Prelude>  :type (>=>)
(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
Prelude>

The return operator transforms regular values into monadic values. It can be used to define a function to convert regular functions into monadic functions.

Bind takes two arguments, the left and right operands, and returns a compound action. The meaning of this new action is to first perform the action to the left. The left action produces a value of type a. Then the function to the right is applied with this value, which returns a second action. Finally the second action is performed. The value it produces becomes the result of the whole compound action. It is common to use the letter k for the second argument of (>>=) bind because k stands for “continuation”.

Prelude> :type (>>)
(>>) :: Monad m => m a -> m b -> m b
Prelude>

The >> function read as chevron is a binding operator that ignores the value of its first action and returns as an overall result the result of its second action only.

Prelude> :type return
return :: Monad m => a -> m a
Prelude>

Prelude> :type fail
fail :: Monad m => String -> m a
Prelude>

The kleisli arrow function <- pronounced "drawn from", indicates that the result of an action is being bound. The way the kleisli arrow  function <- binding operator is processed is that the name on the left-hand side of the kleisli arrow  function <- becomes a parameter of subsequent operations, represented as one large IO action.

Any IO action that you write in a do statement, or use as a parameter for either the >> chevron function or the >>= bind function operators, is an expression returning a result of type IO a for some type a.

The unit type () is an empty tuple which has both a value and type of (). The unit type is similar to a null type or void in other languages.

IO is a monad, which is a type class. An instance of the monad type class, must include the bind functions (>>=) and return. The monad serves as the glue which binds together the actions in a program.

The use of the name main is important. main is defined to be the entry point of a Haskell program. main always has a type signature of main :: IO something, where something is some concrete type. By convention, we do not usually specify a type declaration for main.

If we are taking data out of an IO action, we can only take it out when we are inside another IO action. In other words, to get the value out of an IO action, you have to perform it inside another IO action by binding it to a name with the <- kleisli arrow function. Variables bound by the <- kleisli arrow function are lambda bound and are thus monomorphic. IO actions will only be performed when they are given a name of main or when they are inside a bigger IO action that was composed with a do block. Use the <- kleisli arrow function when you want to bind results of IO actions to names. Use let bindings to bind pure expressions, normal non-IO expressions, to names. Within do blocks or list comprehensions let without in introduce local bindings.

Pure function application should use let instead of the binding kleisli arrow <-.

You are always writing code in the IO monad when you are using GHCi.  GHCi evaluates the IO action, then calls show on the IO action, and prints the string to the terminal using putStrLn implicitly. 

Prelude>  :type when
when :: Monad m => Bool -> m () -> m ()
Prelude>  

when takes a Boolean value and an IO action, if that Boolean value is True, it returns the same IO action that we supplied to it. However, if it's False, it returns the return () action, an IO action that does not do anything.

Prelude> :type sequence
sequence :: Monad m => [m a] -> m [a]
Prelude>

sequence takes a list of IO actions and returns an IO action that will perform those actions one after the other. The result contained in that IO action will be a list of the results of all the IO actions that were performed.

Prelude> :type mapM
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
Prelude>

mapM takes a function and a list, and maps the function over the list, and then sequences it.

Prelude> :type mapM_
mapM_ :: Monad m => (a -> m b) -> [a] -> m ()
Prelude>

mapM_ does the same as mapM; however, mapM_ throws away the result later.

Prelude> :type forever
forever :: Monad m => m a -> m b
Prelude>

forever takes an IO action and returns an IO action that just repeats the IO action it received forever.

Prelude> :type forM
forM :: Monad m => [a] -> (a -> m b) -> m [b]
Prelude>

forM  is like mapM, only it has its parameters switched around. The first forM parameter is the list and the second one is the function to map over that list, which is then sequenced.

To get a list of the bindings currently in scope, use the :show bindings command.

A. LOCAL VARIABLES

Haskell also allows for local bindings; which means we can create values inside of a function that only that function can see. Local bindings are created by using a let in declaration. You can provide multiple declarations inside a let; however, make sure they are indented the same amount, or you will have layout problems.

The keywords to look out for here are let, which starts a block of variable declarations, and in, which ends it. Each line introduces a new local variable. The name is on the left of the =, and the expression to which it is bound is on the right.

In general, we will refer to the places within our code where we can use a name as the name's scope. If we can use a name, it is in scope, otherwise it is out of scope. If a name is visible throughout a source file, we say it is at the top level, it is global.

We can use another mechanism to introduce local variables: the where clause. The definitions in a where clause apply to the code that precedes it. It lets us direct our reader's focus to the important details of an expression, with the supporting definitions following afterwards. For example, you can use where to define a local variable scoped over multiple guarded branches.

Haskell's syntax for defining a variable looks very similar to its syntax for defining a function. This symmetry is preserved in let and where blocks; we can define local functions just as easily as local variables. 

We can also define global variables, as well as global functions, at the top level of a source file.

II. CONCLUSION


In this tutorial, you have received an introduction to the Haskell binding operators.

III. REFERENCES


Bird, R. (2015). Thinking Functionally with Haskell. Cambridge, England: Cambridge University Press.

Davie, A. (1992). Introduction to Functional Programming Systems Using Haskell. Cambridge, England: Cambridge University Press.

Goerzen, J. & O'Sullivan, B. &  Stewart, D. (2008). Real World Haskell. Sebastopol, CA: O'Reilly Media, Inc.

Hutton, G. (2007). Programming in Haskell. New York: Cambridge University Press.

Lipovača, M. (2011). Learn You a Haskell for Great Good!: A Beginner's Guide. San Francisco, CA: No Starch Press, Inc.

Thompson, S. (2011). The Craft of Functional Programming. Edinburgh Gate, Harlow, England: Pearson Education Limited.

Thursday, January 31, 2013

HASKELL INPUT AND OUTPUT (IO)

HASKELL INPUT AND OUTPUT (IO)




REVISED: Monday, October 21, 2024




Haskell Input and Output (IO).

Good Haskell style involves separating pure code from code that performs IO.

I.  HASKELL IMPORT

I am asking you to input the following imports so I can use them in examples "I. A." and "I. B." shown below. I also want to demonstrate the dangers of using imports without knowing their contents.

Prelude> import Control.Arrow
Prelude>

Prelude> import Control.Monad
Prelude>

Prelude> import Data.ByteString
Prelude>

Prelude> import Data.Char
Prelude>

Prelude> import Data.List 
Prelude>

Prelude> import System.IO  
Prelude>

Prelude> import System.Directory
Prelude>

Prelude> import System.Environment 
Prelude>

Prelude> import System.IO.Error
Prelude>

Prelude> import System.Random 
Prelude>

A. :SHOW IMPORTS

Prelude> :show imports
import Prelude -- implicit
import Control.Arrow
import Control.Monad
import Data.ByteString
import Data.Char
import Data.List
import System.IO
import System.Directory
import System.Environment
import System.IO.Error
import System.Random
:module +*Main -- added automatically
Prelude>

Notice Data.ByteString is in the above list of imports.

B. :MODULE -M

Where M is the name of the import you want to remove, imports can be removed from the current scope, using the syntax :module -M as shown below:

Prelude> :module -Data.ByteString
Prelude>

Prelude> :show imports
import Prelude -- implicit
import Control.Arrow
import Control.Monad
import Data.Char
import Data.List
import System.IO
import System.Directory
import System.Environment
import System.IO.Error
import System.Random
:module +*Main -- added automatically
Prelude>

Notice Data.ByteString is not in the above list of imports.

The full syntax of the :module command is: 

:module [+|-] [*]mod1 ... [*]modn

Using the + form of the module command adds modules to the current scope, and - removes them.

You cannot add a module to the scope if it is not loaded.

II.  HASKELL "IO ACTIONS"

If you did:

Prelude> import Data.ByteString
Prelude>

Prelude> import System.IO
Prelude>

and did not do:

Prelude> :module -Data.ByteString
Prelude>

as shown above in I., you will receive an "ambiguous occurrence" error message from GHCi when you do the examples shown below. GHCi will not be able to determine which import you are referring to. Therefore, you have to be very careful when using multiple imports. It is a good idea to know everything contained in an import before you use it.

The GHCi prompt operates as if the lines were being typed into a do block in the IO monad.

Prelude> :type putStrLn
putStrLn :: String -> IO ()
Prelude>

putStrLn gives back nothing, ().

Prelude>  :t getLine
getLine :: IO String
Prelude>

getLine gives back a String to use. 

Prelude> :type readFile
readFile :: FilePath -> IO String
Prelude>

IO is Haskell’s way of representing that putStrLn and readFile are not really functions, in the pure mathematical sense, they are “IO Actions”. Typical actions include reading and setting global variables, writing files, reading input, and opening windows.

The -> drawn from notation is a way to get the value out of an action. The (), pronounced “unit”, is an empty tuple indicating that there is no return value from putStrLn. This is similar to "void" in Java or C.

If we are taking data out of an IO action, we can only take it out when we are inside another IO action. In other words, to get the value out of an IO action, you have to perform it inside another IO action by binding it to a name with the -> drawn from arrow function. Variables bound by the -> drawn from arrow function are lambda bound and are thus monomorphic. IO actions will only be performed when they are given the name of main or when they are inside a bigger IO action that was composed with a do block. Therefore, when you want to bind the results of IO actions to names, use the -> drawn from arrow function.

The only way to use things with an IO type is to combine them with functions using do notation which can include if/then/else and case/of. In a do expression, every line is a monadic value. We code do and then we lay out a series of steps, like we would in an imperative program chaining computations. Each of these steps is an IO action, a control flow expression. A statement is either an action, a pattern bound to the result of an action using <-, or a set of local definitions introduced using letBy putting them together with do syntax, we glue them into one IO action. do works with any monad, the action that we get has a type of IO (), because that is the type of the last IO action inside the doWhen sequencing actions with do notation, each “bare” line, lines that do not have a <- in them, must have type IO ().

do can replace the  >> pronounced "then" operator.

Prelude> :type (>>)
(>>) :: Monad m => m a -> m b -> m b
Prelude>

We can use do syntax to glue together several IO actions into one.  The value from the last action in a do block is bound to the do block result.

You always do IO inside a do statement, using the IO monad. This way you are guaranteed that IO actions are executed in order.

$ is pronounced "apply." When a $ is encountered, the expression on its right is applied as the parameter to the function on its left. In other words the $ is the same as a ( and the closing ) is the end of the line. For example (1, 2, 3, 4) is the same as $ 1, 2, 3, 4

You cannot use $ if the closing ) would not appear at the end of the line.

A. REVIEW

First, we will review GHCi "ambiguous occurrence" error messages

Try doing the following examples. You will receive another GHCi "ambiguous occurrence" error message. The point I am repeatedly trying to make is you should understand the contents of imports before using them.

Exit out of GHCi and then reload GHCi. Use the following commands to see what you start out with in GHCi when it is first loaded.

Prelude>  :show
options currently set: +t   -- +t prints type after evaluation
base language is: Haskell2010
with the following modifiers:
  -XNoDatatypeContexts
  -XNondecreasingIndentation
GHCi-specific dynamic flag settings:
other dynamic, non-language, flag settings:
  -fimplicit-import-qualified
warning settings:
Prelude>

Prelude>  :show imports
import Prelude -- implicit
Prelude>  

Second, we will show a basic example of putStrLn:

Prelude> :{
Prelude| do {
Prelude| putStrLn "Hello, World!"
Prelude| }
Prelude| :}
Hello, World!
it :: ()
Prelude>

Third, we will show an example of using getLine and $ with putStrLn:

Prelude> :{
Prelude| let prRev = do {
Prelude|      inp <- getLine;
Prelude|      putStrLn $ reverse inp;
Prelude| }
Prelude| :}
Prelude| 
Prelude>

Prelude> :type prRev
prRev :: IO ()
Prelude>

Prelude> prRev
Hello, World!
!dlroW ,olleH
Prelude>

B. OUTPUT FUNCTIONS

These functions write to the standard output device, which is normally the user's terminal.

Prelude> import Data.Char
Prelude>

Prelude> :type print
print :: Show a => a -> IO ()
Prelude>

Prelude> :type putChar
putChar :: Char -> IO ()
Prelude>

The unit type (), which is similar to void in other languages, is used by actions which return no values.  return packs up a value into an IO box. <- extracts the value out of an IO box and is read as "comes from". In other words, return turns a value into an IO action and <- turns an IO action into a regular value.

"Copy paste" the following Haskell script into your text editor and "save as" TestReturn.hs  to your working directory:

-- TestReturn.hs

module TestReturn where

main = do putStrLn "Shall I quit the program? y/n"
          ans <- getLine
          if ans /= "y"
             then do putStrLn "Thank you! I enjoy working with you!"
                     main
             else do putStrLn "Nice working with you my friend! Happy trails, until we meet again!"
                     return ()  -- We created an IO that does not do anything so the program can exit.

As shown below, load the TestReturn program, then run the module TestReturn function main, in GHCi:

Prelude>  :load TestReturn
[1 of 1] Compiling TestReturn       ( TestReturn.hs, interpreted )
Ok, modules loaded: TestReturn.
Prelude>

Prelude>  main
Shall I quit the program? y/n
n
Thank you! I enjoy working with you!
Shall I quit the program? y/n
y
Nice working with you my friend! Happy trails, until we meet again!
it :: ()
Prelude>  

PS C:\Users\User\tinnel\Haskell 2024\newProjects> ghci
GHCi, version 9.4.8: https://www.haskell.org/ghc/  :? for help
ghci> :load TestReturn.hs
[1 of 1] Compiling TestReturn       ( TestReturn.hs, interpreted )
Ok, one module loaded.
ghci> main
Shall I quit the program? y/n
n
Thank you! I enjoy working with you!
Shall I quit the program? y/n
y
Nice working with you my friend! Happy trails, until we meet again!
ghci>

putStrLn is a function that takes a single argument of type String and produces a value of type IO (). The IO () signifies an executable action that returns a value of type (). When we supply putStrLn with a String argument we get an executable IO action which will print the supplied String.

Prelude> :type putStr
putStr :: String -> IO ()
Prelude>

Prelude> putStr "Hello"
Helloit :: ()   -- Notice that two lines combine without a \n newline escape sequence!
Prelude> 

Prelude> putStr "Hello\n"
Hello   -- The \n newline escape sequence does a carriage return.
it :: ()Prelude>

Prelude> :type putStrLn
putStrLn :: String -> IO ()
Prelude>

Prelude> :type putStrLn "abcdefghijklmnopqrstuvwxyz"
putStrLn "abcdefghijklmnopqrstuvwxyz" :: IO ()
Prelude>

C. INPUT FUNCTIONS

These functions read input from the standard input device, which is normally the user's keyboard. 

Prelude> :type getChar
getChar :: IO Char
Prelude>

When the function getChar is invoked, it will perform an action which returns a character.

Prelude> :type getLine
getLine :: IO String
Prelude>

Should be read as, "getLine is an action that, when performed, produces a string".

Prelude> :type getContents
getContents :: IO String
Prelude>

Prelude> :type interact
interact :: (String -> String) -> IO ()
Prelude>

Prelude> :type readIO
readIO :: Read a => String -> IO a
Prelude>

Prelude> :type readLn
readLn :: Read a => IO a
Prelude>

Use let bindings to bind pure expressions, normal non-IO expressions, to names. For example:

Prelude> let y = 12
y :: Num a => a
Prelude>

Prelude> y
12
it :: Num a => a
Prelude>

As shown above, let bindings do not automatically print the value bound.

As shown below, when we type out a monadic IO action in GHCi and press Enter, the IO action will be performed:

Prelude> putStrLn "Hello, World!" 
Hello, World!
it :: ()
Prelude>

You are always writing code in the IO monad when you are using GHCi.  GHCi evaluates the IO action, then calls show on the IO action, and prints the string to the terminal using putStrLn implicitly. 

D. IO EXAMPLE 1

"Copy, Paste" the following program into your text editor and "File, Save As" Point.hs to your working directory:

-- Point.hs

module Point where

point :: IO (Char,Char)
point = do {  x <- getChar
                    ; getChar
                   ; y <- getChar
                   ; return (x,y)
                 }

Load Point.hs into GHCi as follows:

Prelude>  :load Point.hs
[1 of 1] Compiling Main             ( point.hs, interpreted )
Ok, modules loaded: Main.
Prelude>

From GHCi call the Point.hs program as follows:

Prelude>  Point.hs
8  -- You will be prompted twice. At each prompt type an Integer then press Enter.
2
('8','2')  -- The output will return a tuple point :: IO (Char,Char).
Prelude>

PS C:\Users\User\tinnel\Haskell 2024\newProjects> ghci
GHCi, version 9.4.8: https://www.haskell.org/ghc/  :? for help
ghci> :load Point.hs
[1 of 1] Compiling Point            ( Point.hs, interpreted )
Ok, one module loaded.
ghci> point
8
2
('8','2')
ghci> :quit
Leaving GHCi.
PS C:\Users\User\tinnel\Haskell 2024\newProjects> 

E. IO EXAMPLE 2

"Copy, Paste" the following program into your text editor and "File, Save As" Name.hs to your working directory:

-- Name.hs

module Name where

import Data.List()
import System.IO

main :: IO ()
main = do {  putStrLn "What is your name?"
                    ; yn 
 <- getLine
<- getline="" span="">                     ; putStrLn ("Hello " ++ yn ++ "!" ) 
                  }

Load Name.hs into GHCi as follows:

Prelude>  : load Name.hs
[1 of 1] Compiling Main             ( name.hs, interpreted )
Ok, one module loaded.
*Main>

From GHCi call the Name program as follows:

*Main>  main
What is your name?
Ron
Hello Ron!
*Main> 

PS C:\Users\User\tinnel\Haskell 2024\newProjects> ghci
GHCi, version 9.4.8: https://www.haskell.org/ghc/  :? for help
ghci> :load Name.hs
[1 of 1] Compiling Name             ( Name.hs, interpreted )
Ok, one module loaded.
ghci> main
What is your name?
Ron
Hello Ron!
ghci> :quit
Leaving GHCi.
PS C:\Users\User\tinnel\Haskell 2024\newProjects> 

III. TEXT FORMAT

Keep lines to no more than 80 columns.

Do not use tabs.

Use CamelCase, do not use underscores.

Do not leave trailing white space in your lines of code.

Do not use braces and semicolons.

Use spaces for indenting.

Indent code blocks such as "let, where, do, and of" 4 spaces.

Indent "where" keyword 2 spaces.

Indent "where" definitions 2 spaces.

Indent "guards" 2 spaces.

IV. CONCLUSION

In this tutorial, you have received an introduction to Haskell input and output (IO).

V. REFERENCES

Bird, R. (2015). Thinking Functionally with Haskell. Cambridge, England: Cambridge University Press.

Davie, A. (1992). Introduction to Functional Programming Systems Using Haskell. Cambridge, England: Cambridge University Press.

Goerzen, J. & O'Sullivan, B. &  Stewart, D. (2008). Real World Haskell. Sebastopol, CA: O'Reilly Media, Inc.

Hutton, G. (2007). Programming in Haskell. New York: Cambridge University Press.

Lipovača, M. (2011). Learn You a Haskell for Great Good!: A Beginner's Guide. San Francisco, CA: No Starch Press, Inc.

Thompson, S. (2011). The Craft of Functional Programming. Edinburgh Gate, Harlow, England: Pearson Education Limited.