Showing posts with label Haskell. Show all posts
Showing posts with label Haskell. Show all posts

Monday, October 21, 2024

HASKELL FUNCTION PROOFS

 

HASKELL FUNCTION PROOFS

REVISED: Saturday, September 6, 2025


1. INTRODUCTION

Haskell Functions as Mathematical Functions

In Haskell, functions are conceptualized as mathematical functions, mapping inputs to outputs. This means they take one or more values as arguments and produce a single result.

Pure Functions

Haskell functions are inherently pure, meaning they have the following properties:

No side effects: They don't modify external state or produce observable effects beyond their return value.

Referential transparency: Replacing a function call with its result doesn't change the program's behavior.

First-Class Citizens

Functions in Haskell are first-class citizens, treated like any other data type:

Passed as arguments: You can pass functions as arguments to other functions.

Returned as values: Functions can return other functions as results.

Assigned to variables: You can store functions in variables.

Syntax and Types

Definition: Functions are defined using the = operator.

Type annotation: The type of a function is specified after its name, using ::. For example, f :: Int -> Int indicates that f takes an integer as input and returns an integer.

Arguments: Multiple arguments are separated by spaces.

Return value: The return value is the expression on the right side of the = operator.

Examples

Simple function:

add :: Num a => a -> a -> a
add x y = x + y

Function taking multiple arguments:

multiply :: Num a => a -> a -> a -> a
multiply x y z = x * y * z

Function returning a function:

makeAdder :: Num a => a -> a -> a
makeAdder x = (\y -> x + y)

Higher-Order Functions

Haskell supports higher-order functions, taking functions as arguments or returning functions as results. This enables powerful programming techniques like:

Map: Applies a function to each element of a list.

map :: (a -> b) -> [a] -> [b]

Filter: Selects elements from a list based on a predicate function.

filter :: (a -> Bool) -> [a] -> [a]

Reduce: Combines elements of a list using a binary function.

reduce :: Integral a => a -> a -> Ratio a

Recursion

Recursion is a common programming technique in Haskell, where a function calls itself to solve smaller instances of a problem. Haskell's type system and pattern matching make it well-suited for recursive functions.

Benefits of Haskell Functions

Readability: Haskell's functional style and concise syntax make code more readable and easier to understand.

Correctness: Pure functions are easier to reason about and test for correctness.

Concurrency: Haskell's functional nature makes it well-suited for concurrent programming.

Modularity: Functions can be composed to build complex programs from smaller, reusable components.

By understanding these concepts, you can effectively use Haskell functions to write elegant, efficient, and maintainable code.

2. PROOFS

Computer program function proofs are mathematical proofs partially or fully generated by a computer program.

You can evaluate (prove) Integer arithmetic; however, computers still have problems with Floating-Point arithmetic. 

Before assuming a Lemma check it with QuickCheck. 

A function algorithm proof is a step-by-step procedure or formula for calculations, problem-solving operations, or accomplishing a task in a certain number of steps.

There are four basic proof techniques used in mathematics: Direct Proof, Proof by Contradiction, Proof by Induction, and Proof by Contrapositive.

PROOF BY INDUCTION EXAMPLE

Proving a recursion reverse function:

sum (reverse [])       = sum []                                               -- def reverse base case
sum (reverse (x:xs)) = sum (reverse xs ++ [x])                  -- def reverse recursion

Base Case:

sum (reverse []) = sum []

This follows directly from the definition of reverse and sum.

Recursive Case:

Inductive Hypothesis: Assume that sum (reverse ys) = sum ys for all lists ys.

Inductive Steps:

sum (reverse (x:xs)) = sum (reverse xs ++ [x])       -- definition of `reverse`
sum (reverse (x:xs)) = sum (reverse xs) + sum [x]  -- lemma (proven below separately)
sum (reverse (x:xs)) = sum xs + sum [x]                  -- inductive hypothesis
sum (reverse (x:xs)) = sum xs + x                             -- definition of `sum`
sum (reverse (x:xs)) = sum (x:xs)                             -- definition of `sum`

Key Points:

The proof relies on a strong inductive hypothesis and a correctly proven lemma.
The commutativity of sum is implicitly used, a direct consequence of the definition.
A formal proof of the lemma enhances the rigor of the overall proof.

LEMMA PROOF

The lemma can be proven using the definition of sum and induction on the length of xs.

Proving the Lemma: sum (reverse xs ++ [x]) = sum (reverse xs) + sum [x]

Proof by Induction on the Length of xs:

Base Case:

When xs = []:

sum (reverse [] ++ [x]) = sum ([x]) = x
sum (reverse []) + sum [x] = sum [] + sum [x] = 0 + x = x

Thus, the equation holds for the base case.

Recursive Case:

Inductive Hypothesis: Assume the equation holds for a list xs of length n. That is:

sum (reverse xs ++ [x]) = sum (reverse xs) + sum [x]

Inductive Steps:

Consider a list (y:xs) of length n+1:

sum (reverse (y:xs) ++ [x])
= sum (reverse xs ++ [y] ++ [x])          -- Definition of `reverse`
= sum (reverse xs ++ [y + x])               -- Definition of `++` and `sum`
= sum (reverse xs) + sum [y + x]          -- Inductive Hypothesis
= sum (reverse xs) + (y + x)                   -- Definition of `sum`
= (sum (reverse xs) + y) + x                   -- Associativity of `+`
= (sum (reverse xs) + sum [y]) + x         -- Definition of `sum`
= sum (reverse xs) + (sum [y] + x)         -- Associativity of `+`
= sum (reverse xs) + sum [y, x]               -- Definition of `sum` and `++`
= sum (reverse xs) + sum [x, y]               -- Commutativity of `+`
= sum (reverse xs) + sum [x] + sum [y]  -- Definition of `sum`
= sum (reverse xs) + sum [x]                    -- Definition of `sum` for a singleton list

Therefore, by the principle of mathematical induction, the lemma holds for all lists xs.

3. CONCLUSION

The concept of pure functionality in Haskell makes its code easier to prove correct than imperative languages.


4. 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.




Monday, January 22, 2024

HASKELL LOGARITHMS

HASKELL LOGARITHMS

REVISED: Sunday, October 13, 2024


1. INTRODUCTION

As mentioned in previous tutorials, Haskell has three exponentiation operators: (^), (^^), and (**). ^ is non-negative integral exponentiation, ^^ is integer exponentiation, and ** is floating-point exponentiation.

Use the ^ exponent operator when you need a non-negative integer as the exponent.

Prelude>  2 ^ 5
32
it :: Num a => a
Prelude>

Prelude>  :type (^)
(^) :: (Num a, Integral b) => a -> b -> a
Prelude>

Prelude>  :info (^)
(^) :: (Num a, Integral b) => a -> b -> a -- Defined in `GHC.Real'
infixr 8 ^
Prelude>    

Prelude>  2 ^ (-5)
*** Exception: Negative exponent
Prelude>

Use the ^^ exponent operator when you need a positive or negative integer as the exponent.

Prelude>  2 ^^ 5
32.0
Prelude>

Prelude>  :type (^^)
(^^) :: (Integral b, Fractional a) => a -> b -> a
Prelude>  

Prelude>  :info (^^)
(^^) :: (Fractional a, Integral b) => a -> b -> a
  -- Defined in ‘GHC.Real’
infixr 8 ^^
Prelude>  

Prelude>  2 ^^ (-5)
3.125e-2
Prelude>

Use the ** exponent operator when you need a positive or negative floating point number as the exponent.

Prelude> 2 ** 5.0
32.0
it :: Floating a => a
Prelude> 

Prelude>  :type (**)
(**) :: Floating a => a -> a -> a
Prelude>

Prelude>  :info (**)
class Fractional a => Floating a where
  ...
  (**) :: a -> a -> a
  ...
         -- Defined in `GHC.Float'
infixr 8 **
Prelude>  

Prelude>  2 ** (-5.0)
3.125e-2
Prelude>

Use the exp exponent operator to use Euler's number e,  the base of the Natural Logarithms.

Logarithms are another way of thinking about exponents. For example, in exponential form 2 3 = 8, and in logarithmic form log 2 8 = 3. In other words, if b x  = y, log b y = x where the base is b and the exponent is x.

Furthermore:

log b (xy) = log b x + log b y

log b (x/y) = log b x - log b y

log b (x y) = y (log b x)

Prelude>  exp 1
2.718281828459045
it :: Floating a => a
Prelude> 

Prelude>  :type exp
exp :: Floating a => a -> a
Prelude>

Prelude>  :info exp
class Fractional a => Floating a where
  ...
  exp :: a -> a
  ...
  -- Defined in `GHC.Float'
Prelude>  

Prelude>  let e = exp 1
e :: Floating a => a
Prelude>

= denotes definition, like it does in mathematics; e.g., e "is defined as" exp 1.

Prelude>  e
2.718281828459045
it :: Floating a => a
Prelude>

Haskell provides various approaches to solving logarithmic equations, depending on the complexity and desired accuracy.

2. OVERVIEW
 
The following are three common methods which can be used to solve logarithmic equations.

2.1. Using Logarithmic Properties:

This method exploits the logarithmic properties, such as product rule, quotient rule, and power rule, to manipulate the equation into a form where the unknown variable can be isolated.

2.2. Using Numerical Methods:

For more complex equations or when analytical solutions are difficult, numerical methods like bisection method or Newton-Raphson method can be applied. These methods iteratively refine an initial guess until the desired accuracy is reached.

2.3. Using Symbolic Libraries:

Libraries like "symbolic" or "polyglot" allow symbolic manipulation of expressions, enabling you to solve equations for exact solutions without numerical approximations.

3. EXAMPLES

3.1. Using Logarithmic Properties:

--  logProp.hs

import Prelude hiding ((**))

-- Function to apply a logarithmic property
applyProperty :: (Double -> Double) -> Double -> Double -> Double
applyProperty f x y = f (x * y)

-- Example equation: 3^x = 81
solveEquation :: Double -> Double
solveEquation x =
  let
    leftSide = exp (x * logBase 10 3)  -- Apply power rule
    rightSide = 81
  in
    logBase 10 rightSide / logBase 10 3  -- Apply quotient rule

main :: IO ()
main = do
  let solution = solveEquation 1.0  -- Starting with a guess of 1.0
  print solution
 
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
PS C:\Users\User> ghci
GHCi, version 9.4.8: https://www.haskell.org/ghc/  :? for help
ghci> :cd C:\Users\User\tinnel\Haskell 2024\newProjects
ghci> :l logProp.hs
[1 of 2] Compiling Main             ( logProp.hs, interpreted )
Ok, one module loaded.
ghci> main
4.0
ghci>

Explanation:

Import Prelude: This module provides basic functions and types, including logBase and exp for logarithms and exponents.

applyProperty: This function demonstrates applying a logarithmic property to two numbers. It's not used directly in this example, but it illustrates the concept.

solveEquation: Takes a guess for x as input. Calculates the left side of the equation using exp (x * logBase 10 3), applying the power rule of logarithms. Compares the left side to the right side (81). Applies the quotient rule of logarithms to isolate x and return its value.

main: Calls solveEquation with an initial guess of 1.0. Prints the solution, which should be 4.0 (since 3^4 = 81).

Key Points:

The program uses logBase 10 for base-10 logarithms. You can use a log for natural logarithms (base e).

It assumes a simple equation with known values, but this approach can be applied to more complex equations as well.

You should use numerical methods to solve equations or find roots in more sophisticated cases. 

3.2. Using the Bisection Method:

-- bisecMeth.hs

import Prelude hiding ((**))

-- Function representing the logarithmic equation
eqn :: Double -> Double
eqn x = log x - 2  -- Example: log(x) = 2

-- Bisection method
bisection :: Double -> Double -> Double -> Double -> Double
bisection a b tolerance iterations =
  let mid = (a + b) / 2
      fmid = eqn mid
      error = abs (b - a)
  in
    if error < tolerance || iterations == 0
      then mid
      else if fmid * eqn a < 0
          then bisection a mid tolerance (iterations - 1)
          else bisection mid b tolerance (iterations - 1)

main :: IO ()
main = do
  let solution = bisection 1.0 10.0 1e-6 100  -- Initial interval, tolerance, max iterations
  print solution

Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
PS C:\Users\User> ghci
GHCi, version 9.4.8: https://www.haskell.org/ghc/  :? for help
ghci> :cd C:\Users\User\tinnel\Haskell 2024\newProjects
ghci> :l bisecMeth.hs
[1 of 2] Compiling Main             ( bisecMeth.hs, interpreted )
Ok, one module loaded.
ghci> main
7.38905593752861
ghci>

Explanation:

Import Prelude: Provides basic functions and types, including log for logarithms.

eqn: Represents the logarithmic equation to be solved.

bisection: Implements the bisection algorithm: Takes the initial interval (a, b), tolerance, and maximum iterations as input. Recursively halves the interval based on the sign of fmid * eqn a. Checks for convergence based on error or maximum iterations.

main: Calls bisection with an initial interval of (1.0, 10.0), tolerance of 1e-6, and maximum iterations of 100. Prints the solution, which should be approximately 7.389065 (since log(7.389065) ≈ 2).

Key Points:

The bisection method is generally more robust than the Newton-Raphson method, as it doesn't require calculating a derivative and can handle a wider range of equations. It converges slower than Newton-Raphson but is guaranteed to converge if the equation has a root within the initial interval. You can adjust the initial interval, tolerance, and maximum iterations to suit your specific problem.

3.3. Newton-Raphson Method:

-- newRap.hs

import Prelude hiding ((**))

-- Function representing the logarithmic equation
eqn :: Double -> Double
eqn x = log x - 2  -- Example: log(x) = 2

-- Derivative of the equation
eqnDeriv :: Double -> Double
eqnDeriv x = 1 / x

-- Newton-Raphson method
newtonRaphson :: Double -> Double -> Double -> Double
newtonRaphson x0 tolerance iterations =
  let x1 = x0 - eqn x0 / eqnDeriv x0
      error = abs (x1 - x0)
  in
    if error < tolerance || iterations == 0
      then x1
      else newtonRaphson x1 tolerance (iterations - 1)

main :: IO ()
main = do
  let solution = newtonRaphson 1.0 1e-6 100  -- Initial guess, tolerance, max iterations
  print solution

PS C:\Users\User> ghci
GHCi, version 9.4.8: https://www.haskell.org/ghc/  :? for help
ghci> :cd C:\Users\User\tinnel\Haskell 2024\newProjects
ghci> :l newRap.hs
[1 of 2] Compiling Main             ( newRap.hs, interpreted )
Ok, one module loaded.
ghci> main
7.389056098930626
ghci>

Explanation:

Import Prelude: Provides basic functions and types, including log for logarithms.

eqn: Represents the logarithmic equation to be solved.

eqnDeriv: Calculates the derivative of the equation, needed for the Newton-Raphson method.

newtonRaphson: Implements the Newton-Raphson algorithm: Takes the initial guess, tolerance, and maximum iterations as input. Recursively updates the guess using the formula x1 = x0 - eqn x0 / eqnDeriv x0. Checks for convergence based on error or maximum iterations.

main: Calls newtonRaphson with an initial guess of 1.0, tolerance of 1e-6, and maximum iterations of 100. Prints the solution, which should be approximately 7.389056 (since log(7.389056) ≈ 2).

Key Points:

You can adjust the equation (eqn), derivative (eqnDeriv), tolerance, and maximum iterations to solve different logarithmic equations.

The method might not converge for all equations or initial guesses.

Other numerical methods, like the bisection method, can also be implemented in Haskell using similar techniques.

3.4. Symbolic Libraries:

While Haskell doesn't have a library named "symbolic" specifically for symbolic computation, it does have several libraries that offer symbolic capabilities:

3.4.1. SBV (Satisfiability Modulo Theories):

Provides symbolic reasoning for expressions and constraints. Can be used to solve equations, prove theorems, and model systems.

-- satModTh.hs

-- The Data.SBV module is not part of the standard Haskell libraries and can not currently be imported. However, the following program is shown and can be used when its import is available.

import Data.SBV

solveEquation :: SymExpr Solver -> IO ()
solveEquation expr = do
    model <- getModel expr
    print (eval model expr)

main :: IO ()
main = do
    let x = free "x" :: SymExpr Solver
    solveEquation (log x .== 2)

3.4.2. Grisette:

Offers symbolic evaluation of Haskell expressions. Useful for constraint solving, testing, and analysis.

-- grisette.hs

-- Data.Grisette.Crucible.Core can not currently be imported; however, the following program is shown and can be used when its import is available.

import Data.Grisette.Crucible.Core

solveEquation :: Expr -> IO ()
solveEquation expr = do
    result <- evaluate expr
    print result

main :: IO ()
main = do
    let x = mkVar "x" :: Expr
    solveEquation (log x .== 2)

3.4.3. Polyglot:

A frontend for various symbolic backends (e.g., Mathematica, SymPy).  Allows interaction with these systems from Haskell.

-- polyglot.hs

-- Language.Polyglot can not currently be imported; however, the following program is shown and can be used when its import is available.

import Language.Polyglot

solveEquation :: String -> IO ()
solveEquation expr = do
    result <- runSession $ runMathematica $ expr
    print result

main :: IO ()
main = do
    solveEquation "Solve[Log[x] == 2, x]"

Choosing the right library depends on your specific requirements and the complexity of the symbolic tasks you need to perform.

4. CONCLUSION

Choose the method based on the equation's complexity and desired accuracy.

Numerical methods may require fine-tuning initial guess and tolerance parameters.

Symbolic libraries offer exact solutions but might not be efficient for all cases.

5. 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.

Saturday, November 18, 2023

HASKELL AI DEVELOPMENT

HASKELL AI DEVELOPMENT

REVISED: Sunday, October 13, 2024


1. INTRODUCTION

Haskell, a purely functional programming language, holds significant relevance in the realm of artificial intelligence (AI) due to its unique characteristics that align well with AI principles and applications.

2. OVERVIEW

Here's a closer look at the key reasons why Haskell is considered a valuable tool for AI development:

Type System and Safety: Haskell's strong static typing system ensures that programs are rigorously checked for type errors at compile time, significantly reducing runtime errors and enhancing code reliability. This is crucial for AI applications, where complex algorithms and data structures require a high degree of precision.

Purity and Modularity: Haskell's pure functional programming paradigm promotes modularity and composability, making it easier to break down complex AI problems into smaller, well-defined functions. This modular approach simplifies reasoning about code, improves maintainability, and facilitates collaboration among developers.

Abstraction and Expressiveness: Haskell's expressive typing and higher-order functions allow for the creation of concise and abstract code, capturing the essence of AI problems without getting bogged down in implementation details. This abstraction leads to more maintainable and adaptable code.

Laziness and Efficiency: Haskell's lazy evaluation mechanism ensures that expressions are evaluated only when their values are needed, leading to efficient memory usage and performance optimization. This is particularly beneficial for AI applications that deal with large datasets and computationally intensive algorithms.

Concurrency and Parallelism: Haskell's support for concurrency and parallelism makes it well-suited for developing AI algorithms that can utilize multicore processors and distributed computing systems. This is essential for handling the growing demands of real-time AI applications.

Domain-Specific Languages (DSLs): Haskell's ability to define DSLs enables AI developers to create specialized languages tailored to specific AI domains, such as machine learning, natural language processing, and robotics. This customization enhances the expressiveness and efficiency of AI code.

Research and Innovation: Haskell's active research community and growing adoption in academia contribute to the development of new tools, libraries, and techniques specifically designed for AI applications. This fosters innovation and continuous improvement in AI development using Haskell.

3. CONCLUSION

While Haskell may not be as widely used in industry as other languages like Python or R for AI development, its unique strengths and the growing interest in functional programming make it a promising choice for tackling the challenges of modern AI. As AI continues to evolve, Haskell's potential to contribute to the development of robust, reliable, and efficient AI systems is likely to grow.

4. 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, June 1, 2023

HASKELL IS A TOOL

HASKELL MONAD OVERVIEW

REVISED: Monday, October 7, 2024


1. INTRODUCTION

Haskell is a powerful programming language that is well-suited for a variety of tasks.

2. OVERVIEW

For example Haskell is well-suited for tasks including:

Functional programming: Haskell is a purely functional language, which means that it does not have side effects. This makes it ideal for tasks that require a high degree of mathematical precision, such as symbolic computation and artificial intelligence.

Concurrent programming: Haskell has built-in support for concurrent programming, which makes it ideal for tasks that require multiple threads of execution, such as web servers and distributed systems.

Data analysis: Haskell has a rich set of libraries for data analysis, making it ideal for tasks such as data mining and machine learning.

However, Haskell is not without its drawbacks. It can be difficult to learn, and it is not as widely used as other programming languages, such as Java and Python. This can make it difficult to find help and resources when you are using Haskell.

Here are some tasks that are better suited for other programming languages:

Web development: Haskell is not a good choice for web development. There are few web frameworks available for Haskell, and the language's performance can be a bottleneck for high-traffic websites.

Graphics programming: Haskell is not a good choice for graphics programming. The language does not have built-in support for graphics libraries, and its performance can be a bottleneck for graphics-intensive applications.

Game development: Haskell is not a good choice for game development. The language does not have built-in support for game engines, and its performance can be a bottleneck for games with complex graphics and physics.

3. CONCLUSION

Overall, Haskell is a powerful programming language that is well-suited for a variety of tasks. However, it is important to choose the right tool for the job. If you are working on a task that requires a high degree of mathematical precision, concurrent programming, or data analysis, then Haskell is a good choice. However, if you are working on a task that is better suited for a different programming language, then you should choose that language instead.

4. 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.

Tuesday, May 30, 2023

HASKELL MONAD OVERVIEW

HASKELL MONAD OVERVIEW

REVISED: Sunday, October 13, 2024


1. INTRODUCTION

What is a monad?

A monad is a type constructor we will refer to as M, with two operations we will refer to as return and bind.

The operation return takes a value of type a and returns a value of type M a.

The operation bind takes a value of type M a and a function of type a -> M b, and returns a value of type M b.

The return operation is used to create new monadic values, and the bind operation is used to chain together monadic computations.

2. OVERVIEW

Why use monads?

Monads are a powerful tool for structuring computations. They can represent various computational concepts, such as state, sequencing, and exceptions.

State: Monads can be used to represent computations that have side effects, such as reading or writing to a file.

Sequencing: Monads can be used to represent computations that need to be executed in a specific order.

Exceptions: Monads can be used to represent computations that can fail.

Haskell has many built-in monads, including:

Maybe: This monad represents computations that can fail.

IO: This monad represents computations that have side effects.

State: This monad represents computations that have mutable states.

3. EXAMPLE

Here is an example of how to use the Maybe monad to represent a computation that can fail:

-- mayMon.hs

import Control.Monad()

{-
The function divide x y returns a Maybe Integer, representing the result of dividing two numbers. x is the numerator, y is the denominator. 
-}

divide :: Integer -> Integer -> Maybe Integer
divide x y =
  if y == 0 then Nothing else Just (x `div` y)

{-
The function main uses the `Maybe` monad to print the result of dividing x by y.
-}

main :: IO ()
main = do
  xStr <- readLn
  yStr <- readLn
  result <- divide x y
  case result of
    Just z -> print z
    Nothing -> putStrLn "Division by zero!"

This program effectively handles division by zero using the Maybe monad, providing a Nothing value in such cases and printing an appropriate error message.

Here's a breakdown of the code:

  • import Control.Monad(): Imports the Control.Monad module, which provides essential functions and type classes for working with monads, including Maybe.
  • divide :: Integer -> Integer -> Maybe Integer: Defines a function named divide that takes two Integer arguments (numerator and denominator) and returns a Maybe Integer quotient result.
  • if y == 0 then Nothing else Just (xdivy): Uses a conditional expression to check if the denominator (y) is zero. If it is, the function returns Nothing to indicate an error (division by zero). Otherwise, it returns Just (xdivy), where xdivy performs integer division and Just wraps the result in a Maybe value.
  • main :: IO (): Defines the main function that executes the program's logic.
  • x <- readLn: Reads an Integer value from the standard input and binds it to the variable x.
  • y <- readLn: Reads another Integer value from the standard input and binds it to the variable y.
  • result <- divide x y: Calls the divide function with the values of x and y, binds the result (either Just z or Nothing) to the variable result.
  • case result of: Uses a pattern matching case expression to handle the different possible values of result.
  • Just z -> print z: If result is Just z, it prints the value of z (the result of the division).
  • Nothing -> putStrLn "Division by zero!": If result is Nothing, it prints the error message "Division by zero!".

This program effectively demonstrates the use of the Maybe monad for handling potential errors and providing meaningful feedback to the user.

4. CONCLUSION

Monads are a powerful tool for structuring computations in Haskell. They can be used to represent a wide variety of computational concepts, such as state, sequencing, and exceptions.

This tutorial has helped you to learn how to program using Haskell monads.

5. 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.

Tuesday, June 23, 2015

HASKELL PARSING FILE RECORDS EXAMPLE

HASKELL PARSING FILE RECORDS EXAMPLE

REVISED: Thursday, October 10, 2024




1. INTRODUCTION

Using Haskell to parse records stored on a file is a good way to begin learning Haskell data structures.

2. HASKELL PARSING FILE RECORDS EXAMPLE

This example parses the records in the annual customer sales file sales1945.txt to obtain the total sales for one customer.  You can create the sales1945.txt file from the Annual Sales File Listing printed by the module. Each record in the file has a field for custID (customer identification), invoice (invoice number), usdLines (United States Dollars), and mmddyyyy (invoice date). The example computes the total annual sales to one customer based on the custID.

2.1 Module

{-

customerFilter  -- Function which creates a new list containing all records matching the customer ID.

customerID  -- The variable name for identification of the customer.

customerSales  -- Function which computes the customer total annual sales.

customerSalesFileRecords  -- The variable name containing all of the records for the entire annual sales file.

salesByCustomer  -- Function for user to input sales file name and customer ID.

sales  -- Function which creates a new list of only customer sales amounts.  

total  -- Function which turns each customer sales invoice amount String into a Double and totals the amounts. 

totalCustomerSales  -- The variable name for the total annual customer sales for one customer.

usdLines  -- Function which creates a new list of usdLines.

-}

module Sales where
import Data.List.Split(endBy)
import Data.List(isInfixOf, isPrefixOf)

{-

The function aims to compute total annual USD sales for one customer ID.

The '.' operator is used to compose functions. Function composition is a way to "compose" two functions into a single function.

map takes a function and a list and applies that function to every element in the list, producing a new list.

filter is a function that takes a predicate and a list and then returns the list of elements that satisfy the predicate.

A predicate is a function that tells whether something is true or not.

lines function is defined in the Data.List library. lines takes a String, and breaks it up into a list of strings, splitting on newlines.

dropWhile creates a list from another list taking from it its elements from when the condition fails for the first time till the end of the list.

isInfixOf function takes two lists and returns True iff the first list is contained, wholly and intact, anywhere within the second.

isPrefixOf function takes two lists and returns True iff the first list is a prefix of the second.

endBy creates a new list by splitting a String into chunks terminated by the given subsequence which is "@" in the example below.
 
-}

customerSales :: String -> String -> Double
customerSales customerSalesFileRecords customerID  = total.sales.usdLines $ customerFilter customerSalesFileRecords customerID
                                    where usdLines = map $ filter (isPrefixOf "usd").lines
                                               sales       = map (dropWhile (> ' ')).concat
                                               total        = sum.map (\x -> read x :: Double)
                                               customerFilter customerSalesFileRecords customerID = filter (isInfixOf customerID) $ endBy "@" customerSalesFileRecords

{-

Function purpose is for user to input sales file name and customer ID.

Function prints sales file records.

Function returns total annual USD sales for one customer ID
  
-}
                                    
salesByCustomer :: IO Double
salesByCustomer = do
    putStrLn "Enter Annual Sales File Name:" 
    -- For example, enter sales1945.txt
    customerSalesFileRecords <- getLine >>= readFile
    -- Reads in entire sales1945.txt file
    putStrLn "\nAnnual Sales File Listing:\n"
    putStrLn customerSalesFileRecords                                 
    putStrLn "\nEnd Of Annual Sales File Listing:\n"
    putStrLn "Enter Customer ID:"
    -- For example, a1a
    customerID <- getLine
    return $ customerSales customerSalesFileRecords customerID
    
main :: IO()
main = do
    totalCustomerSales <- salesByCustomer
    putStrLn ("\nCustomer Total USD Sales: " ++ show totalCustomerSales)

2.2 GHCi

GHCi, version 9.8.1: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude>

Prelude>  :load Sales
[1 of 1] Compiling Sales              (Sales.hs, interpreted )
Ok, modules loaded: Sales.
(0.09 secs, 34185296 bytes)
Prelude>

Prelude>  main
Loading package split-0.2.2 ... linking ... done.
Enter Annual Sales File Name:
sales1945.txt

Annual Sales File Listing:

custID a1a
invoice i1
usd 1.01
mmddyyyy 0101i945
@
custID b2b
invoice i2
usd 5678.05
mmddyyyy 02021945
@
custID c3c
invoice i3
usd 910.12
mmddyyyy 03101945
@
custID d4d
invoice i4
usd 1234.11
mmddyyyy 04111945
@
custID d4d
invoice i5
usd 5678.06
mmddyyyy 05211945
@
custID c3c
invoice i6
usd 910.01
mmddyyyy 06081945
@
custID b2b
invoice i7
usd 1111.08
mmddyyyy 11121945
@
custID a1a
invoice i8
usd 1000.03
mmddyyyy 11131945
@
custID a1a
invoice i9
usd 100.01
mmddyyyy 12251945
@
custID a1a
invoice i10
usd 10.02
mmddyyyy 12251945
@

End Of Annual Sales File Listing:


Enter Customer ID:
a1a

Customer Total USD Sales: 1111.07
Prelude>

3. CONCLUSION

In this tutorial, you have seen a basic approach for using Haskell to parse file records.

4. 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.