Haskell Kyle Young

advertisement
Haskell
Kyle Young
I. The History of Haskell
The conception of Haskell happened at Yale University by Peyton Jones, Paul Hudak,
and Philip Wadler; this was a meeting on their way to the Functional Programming and
Computer Architecture conference at Portland, Oregon (Hudak et al.). The meeting at
Yale decided that there should be a meeting at the 1987 FPCA (Functional Programming
and Computer Architecture conference) to design a common lazy functional language,
the need for a common programming language was becoming more and more apparent
for there was already over 12 purely functional with loose sytax and structure created and
being used by the time of the FPCA (Hudak et al.).
The meeting at the FPCA gave birth to Haskell's basic design, but it did not have a
name yet (Hudak et al.). The name came later at the first offical meeting for the common
functional language, the name was decided to be “Curry” in honor of Haskell B. Curry,
but there were too many ways to make fun of a language called Curry, thus they decided
on the name Haskell (Hudak et al.). They asked Haskell's widow, Virginia Haskell, if it
would be okay to name their language after her late husband, and she said yes. At a
speech at Penn State she said “You know Haskell never actually liked the name Haskell.
(Hudak et al.)”
The 1.0 release of Haskell came out in 1990. The 1.1 release came out in 1991, as a
house keeping update, it allowed for let expressions and operator sections. In 1992
release 1.2 came out, then 1.3 came out in 1996 it allowed for constructor classes,
afterwords in 1999 the Haskell 98 Report was released (Hudak et al.). Haskell 98 is what
has been built upon even to this day. In 2002, Haskell 98 report was revised and the book
written by Peyton Jones was released and agreed to be free to read online (Hudak et al.),
and is still free to read and available to this day. The newest version of Haskell is Haskell
2010, though you can still program in Haskell using Haskell 98's standards and syntax.
II. An Overview of Haskell
Names Binding Scopes
Haskell provides an unlimited character count for the naming of variables, functions,
modules, etc... Haskell is case-sensitive and thus provides a wide variety of options when
naming in Haskell. Haskell's naming conventions are usual camel-casing the first part
beginning with a lower-case letter. It is also a common naming convention in Haskell to
name important/long functions with longer names and lesser important/smaller functions
with smaller names.
Haskell has bindings through types and type classes. The types, either pre-existing or
user-created, have a set range and rules that a function is given to follow (Aaby, A.). Then
type classes bind the rules to functions and types that use or build off of those type
classes.
Scopes in Haskell are in global, functions themselves, type classes, and modules. If it
is mentioned globally or outside of a function, modules or a type class it is scoped static,
if it is scoped in functions themselves they are scoped dynamically (Lipovača, Miran).
Data Types
The options for Data Types in Haskell are literally limitless, due to the ability to
make your own Abstract Data Types and modify pre-existing Data Types. Though the
options are endless to start Haskell gives you three “simple” types Boolean, Character,
and Numeric (Aaby, A.). This is branched off to be Bool (Boolean, true or false), Char
(Character), Int, Integer (Int with an arbitrary precision), Float (single precision floating
point), Double (double precision floating point), Bin (binary number), String (considered
a list of characters) (Aaby, A.). Each of the number types can be separated out into 8, 16,
32, 64 bit versions (“Haskell”) i.e.
int a
int8 a
There are also other data types that are not a single value, these include Lists, Tuples,
and Numbers (Aaby, A.) (Lipovača, Miran). Lists and Tuples are considered a composite
type in Haskell. In lists all of the values must be of the same type, contained in square
brackets i.e. “[ ]” and separated by a comma (Aaby, A.) (Lipovača, Miran) (“Haskell”). A
Tuple is a mixed-mode composite type, but is not subscripted and accessed by pattern
matching, they are represented in parentheses i.e. “( )”, and each element is separated by
a comma (Aaby, A.) (“Haskell”) (Matuszek, David).
Here is an example of both:
--This is a list
[“Hello”, “World”, “Always”, “The”, “First”, “Program”]
--This is a Tuple
(“Winter”, True, 42, False)
If you declare something as Numbers you can list a series of numeric types separated
by a comma and you can leave it to Haskell to figure it out . I.e
Numbers a, b, c
let a = 8
let b = 3.2
let c = 8.333333333333383
All of these data types being mentioned and are very important, to write a program in
Haskell there is no need to declare them at all (Aaby, A.) (“Haskell”) (Lipovača, Miran).
The code snippet about could as easily be written as:
let a = 8
let b = 3.2
let c = 8.3333333333333383
This is due to Haskell’s ability to interpret the type on its own, during compile and/or
runtime, depending of the process in which is translating Haskell (Aaby, A.) (Lipovača,
Miran). If you do this method it is recommended that people use the “:t” when trying to
compare or compute with multiple variables (Matuszek, David). The “:t” command
returns a string that explains the type of the variable (Matuszek, David). And example of
this is:
let a = 8
Prelude> :t a
Prelude> a :: Integer
The “:t” command must come before the variable that you wish to know the type. The
“Prelude>” is representing the runtime environment, and depending on what you are
using to compile Haskell you will get “Prelude>” like in the example or “ghci” both are
correct and there are very little differences in how they operate.
Abstract Data Types
Haskell also allows for the creation and use of abstract data types. These data types
can be/do just about anything a user could need within a functional language. There are
three main ways of going about creating abstract data types in Haskell, using type,
newtype, or data (Lipovača, Miran) (“Haskell”). Each of these have their own meaning,
use, and should not/cannot be used interchangeably. The keyword “type” is used to create
a synonym that has the same construct as that of what is set to, an example of this is
(“Haskell”):
type address = String
The abstract data type is address, but it acts just as a string so you can set address
equal to something like “555 main street Great Place, NY, 55555” and it would be just
that a string that would return that value. Then there is the “newtype” keyword which
allows you to create a new data type, but can only have one constructor (“Haskell”). This
can be seen like such:
newtype F = Cint
But this would not work as with the “newtype”
newtype Pair a b = Pair(a, b)
Now because this has two constructors a and b, this line would not compile. The “data”
keyword allows for a very similar thing as the “newtype” but is lazy, so it allows as many
constructors as you want (“Haskell”). So going back to the example that would not
compile with “newtype” it would with this:
data Pair a b = Pair(a, b)
This would compile in Haskell because it is created with the “data” keyword.
Control Structures
There are no control statements in Haskell so everything is an expression, meaning
they require each part of the expression to be mentioned. The control structures are
expressions, these include if expressions, guard expressions, and case expressions. Since
if expressions are constructed to include the initial if, the then and the else or otherwise
statement (“Haskell”) (Lipovača, Miran). This would look something like:
if c >= 10
then “Yay”
else “No”
Guards are essentially top-down if statements or a series of boolean expressions
(“Haskell”). So each solution are piped through to the next one. An example of this is:
describeNumber a
| a < 10 = “Less then or equal to ten”
| a > 10 = “Greater then ten”
| otherwise = “ten”
Case expressions are also available in Haskell, case statements in Haskell act like a
piece-wise function (“Haskell”). So it evaluates a passed value and sees if it matches a
certain value meets the options or returns a default value. Presented in this fashion:
x = case x of
0 → “Woo”
1 → “Yay”
_ → “What?”
Encapsulation and Polymorphism
Encapsulation can occur in Haskell in only one way and that is through modules .
You can create a module and a program import the functions exported from the interface
of that module and inherit the functions and data mentioned in that interface (Jones,
Simon, et al.). Modules can inherit from other Modules (Jones, Simon, et al.). The goal of
making Modules is to make them generic enough so that they can be used by multiple
programs and still be useful (Jones, Simon, et al.).
Polymorphism can happen in Haskell by the way of creating of classes. The use of
type classes are used to create and specify the rules of a type so that it separates the type
and the type class are separate (Jones, Simon, et al.). Thus you could make a class like a
house and apartment and Mansion could build and use the same rules set by the type class
house.
Concurrency
Haskell allows for concurrency, according to Haskell.org you can do this by adding
the “-threaded” tag at the end of your compiling statement when using the command
prompt/ terminal. Functional languages are notorious for their ability for
asynchronization and Haskell is no different.
III. Evaluation of Haskell
This portion of the paper is written entirely of the author's opinion and should not be
taken as fact, but rather an educated opinion.
Readability
Haskell being a functional language its readability suffers greatly. The reason for this,
is that if the program is of any great length or needs a lot of functions the program would
require a lot of scrolling through to a) find anything and b) figure out what that function
is exactly doing. Since a lot of functions will most likely pipe to other function and/or call
other functions it only adds to the back and forth reading requirements of Haskell.
The community standards of writing in Haskell does not help this cause in the least.
Most Haskell programmers use the rule “if it is a small function it is named such,” thus if
the function only sees if a number is even or odd then it might be named something like
“eo” or some just two character name. The function would probably look like something
like this:
eo :: int -> bool
eo a
= if a mod 2
then a = true
else a = false
Just looking at this equation its really hard to get what’s truly happening. What does
eo mean? How does a both equal a int value and a Boolean? The code reads in the value a
as an integer and then returns it as a Boolean value. So if we were to pass a as 2 it would
read in 2 perform modulus division and return true. It is good to note that the true value
of a, in this case being 2, has not changed it has just been interpreted and the value of the
interpretation was returned. This might help when initially writing the code, because we
all hate having to have more required key strokes to write code, but it majorly decreases
the readability of the code.
Writability
Haskell is very lenient when writing code for it does not require many of the error
causing character or keywords like many other languages. Haskell does not require a
semi colon at the end of every line or brackets around a function like many of the C based
languages. It also does not require parentheses around each function and around each part
of a function like many lisp based languages. These things on top of Haskell’s very
strong type system makes it very writable.
Haskell also takes care of a lot of the inner workings for you so you can just worry
about programming. There is no need for allocating, deallocating memory, due to
preludes/ghci’s garbage collection. The simpleness of writing a function and piping the
values through so that you may have your desired output is simple for the functional
programmer when using Haskell.
Reliability
The nature of functional programming languages allow for extreme reliability and
Haskell only adds more to it. By not allowing mutable data, all functions will return the
same value if given the same parameters each time. This comes from the inability to
change the value of a single variable outside of its local function in which it is declared,
the value/result of a function can only be returned or piped to another function.
Another reason why Haskell is reliable, you cannot do mix-mode arithmetic or mixmode structures (aka, lists). Haskell requires all variables/values involved in the
arithmetic to be of the same type, thus if you wanted to even add an Integer and a Double
and wanted it to return a Double you would have to have a function that would take in the
Integer and return that value as a double. Then you would have to have to have a function
that added two doubles and returned a double. This would be done like so:
Int2Double :: Int -> Double
add2Doubles :: Double -> Double -> Double
let a = 5
let b = 5.5
add2Doubles a b = (Int2Double a) + b
There is a major problem with the reliability of Haskell, due to it having no real
control structures, the only form of looping possible is through recursion (Estabrooks,
Wade). If you have any really large amount of data that needs to be looped through you
will need lots of memory or the stack will run out of memory (Estabrooks, Wade). Thus,
the program would crash when dealing with any large amount of data manipulation
(Estabrooks, Wade).
Cost
The upfront cost of using Haskell is nothing, it is completely free. The compiler(s)
and interpreter(s) are free, and there are a lot of IDE’s that are free to use that are built for
programming in Haskell. Haskell’s compilers and interpreters, found on Haskell’s
website, are available for Windows, Mac OS, Linux (Estabrooks, Wade). So the initial
cost for anybody or anything is nothing but a little bit of time to download and path it to
where it is desired to be at.
There is the cost of training programmers to program in Haskell and to be able to
create solutions for the problems the business has in Haskell. Teaching someone Haskell
that has no experience and/or knowledge of Haskell can be a very hard task to do, which
could cost the person/company a lot of money. So that their workers are able to acclimate
to the language. Back to Haskell’s down fall when it comes to big data that has to be
passed through one function/pipe, this would also imply that the place or person would
need to invest in larger amounts of memory so that this would not be as much, if any, of a
problem (Estabrooks, Wade).
Overall
The overall look at Haskell is that is a lazy, strictly-typed, purely functional
programming language that has the ability to do some amazing thing at a very efficient
rate. This being said I believe it is a good teaching language for functional programming
and has it uses in small “real-world” applications that need what functional programming
provides best -- reliability. Haskell is not meant for the big bad world of every day major
project development that is the “real-world”.
Considering the overall lack of consistent updates and its inability to have any real
looping except for recursion makes it very impracticable for large data/ “Big Data”
projects and, in my opinion, are becoming more and more relevant in today’s industry.
I’ve heard about the uses of Haskell in physics and mathematical projects, even some
about it being used in the supercollider, that being said I can’t find much proof of it.
Haskell is a very fun, well made, and, for a functional language, very intuitive.
Though it isn’t worth the cost of switching to Haskell unless your main need, and it is a
very big need, is reliability.
Appendices
Fibonacci Sequence (“Generating Fibonacci...”)
fib :: Integer -> Integer
fib n
| n == 0 = 0
| n == 1 = 1
| n > 1
= fib (n-1) + fib (n-2)
Rock Paper Scissors (“Rock-Paper-Scissors”)
import System.Random
data Choice = Rock | Paper | Scissors
deriving (Show, Eq)
beats
beats
beats
beats
Paper Rock = True
Scissors Paper = True
Rock Scissors = True
_ _ = False
genrps (r,p,s) = fmap rps rand
where rps x | x <= s
= Rock
| x <= s+r = Paper
| otherwise = Scissors
rand = randomRIO (1,r+p+s) :: IO Int
getrps = fmap rps getLine
where rps "scissors" = Scissors
rps "rock" = Rock
rps "paper" = Paper
rps _ = error "invalid input"
game (r,p,s) = do putStrLn "rock, paper or scissors?"
h <- getrps
c <- genrps (r,p,s)
putStrLn ("Player: " ++ show h ++ "
Computer: " ++ show c)
putStrLn (if beats h c then "player
wins\n"
else if beats c h then "player
loses\n"
else "draw\n")
let rr = if h == Rock then r+1 else r
pp = if h == Paper then p+1 else p
ss = if h == Scissors then s+1 else s
game (rr,pp,ss)
main = game (1,1,1)
V. Bibliography
Aaby, A. “Haskell Tutorial” 16 Jan. 2007. Web Page. 20 Oct 2014.
Estabrooks, Wade, et al. “Functional Programming Using Haskell”
28 Nov 1999. Web Page. 19 Oct 2014.
“Haskell”. WIKIBOOKS. 18 June 2014. Web Page. 14 Oct 2014.
Hudak, Paul, et al. "A History of Haskell: Being Lazy With Class."
Microsoft Research. 7 Jun. 2007. PDF. 12 Oct. 2014.
Jones, Simon, et al. “Haskell 98 Report”. Jones, Simon. Sept 2002. PDF.
12 Oct. 2014.
Lipovača, Miran. “Learn You a Haskell for Great Good! A beginner’s guide”
Apr. 2011. Web Page. 12 Oct. 2014.
Matuszek, David. “A Concise Guide to Haskell”. Np. 2010. HTML.
12 Oct 2014.
“Generating Fibonacci numbers in Haskell?”. Stack Exchange. Jul. 2013. HTML
3 Nov 2014.
“Rock-Paper-Scissors”. RosettaCode.com. 5 Oct 2014. HTML. 3 Nov 2014.
Download