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.