Haskell 101: Установка, Выражения, Типы
WelcomeДобро toпожаловать partв 1первую ofчасть theсерии MondayОтрыва MorningПонедельнечного Хаскельного Утра! Если вы мечтали попробовать изучить Haskell,но никогда не могли найти хорошее руководство для этого, вы в правильном месте! У вас может не быть знания об этом прекрасном языке. Но после прочтения трех статей, вы должны будете знать базовые идеи достаточно, чтобы начать программировать самостоятельно.
Эта статья покрывает несколько различных тем. Первая, мы скачаем всё необходимое и установи. Затем мы начнем писать наше первое выражение и изучим немного про систему типов в Haskell. Дальше, мы поместим "функцию" в функциональное программирование и изучик что Haskell Liftoffфункции Series!являются Ifобъектом you'veпервого alwaysкласса. wantedНаконец, toмы tryзатронем learningтему более сложных типов таких как списки и кортежи.
Если вы уже читали эту стать или знакомы со всеми этими концептами, вы можете перепрыгнуть ко второй части. В ней мы поговорим о написании своих фалов с кодаи и написании более сложных функций с некоторым дополнительным синтаксом. Обязательно загляните в главу 3, где мы посмотрим на то, как легко создавать свой собственный тип данных!
Эта серия, так же, с примерами в репозитории Github. Этот репозиторий позволит вам работать с некоторыми примерами кода из этих статей. В этой первой части, мы в основном будет работать с GHCI, нежели с файлами.
Наконец, как только вы закончите с этим, проверьте себя с помощью чеклиста. Это даст вам возможность проверить свои знания со всех сторон.
Установка
Если вы еще не касались Haskell butсовем, wereпервый neverшаг able- toскачать findплатформу aHaskell. goodСкачаем tutorialпоследнюю forверсию it,для you'reвашей inОС theи rightпроследуем place!по Youподсказкам mightна have zero knowledge about this awesome language right now. But after these three articles, you should know the basic concepts well enough that you can start programming on your own.экране.
ThisПлатфрма articleсодержит will4 coverглавных aсущности. fewПервая different- topics.GHC
, Firstшироко we'llраспространненый downloadкомпилятор allHaskell. theКомпилятор toolsэто weто, needчто andпревращает installкод them.в Thenчто-то we'llчто startкомпьютер writingможет ourзапустить. firstВторое expressions- andGHCI
, learningинтерпретатор aдля bitязыка aboutHaskell. Haskell'sОн typeпозволяет system.вам Next,вводить we'llвыражения putи theтестировать "function"некоторые inвычисления functionalбез programmingтого, andчтоб learnиспользовать howотдельный Haskell's functions are first class citizens. Finally, we'll wrap up by talking about some slightly more complicated types like lists and tuples.файл.
IfТретье you've- alreadyCabal
, readменеджер thisзависимости articleдля orHaskell areбиблиотек. familiarОн withпозволяет allвам theseскачивать concepts,код, youкоторый shouldдругие jumpлюди straightуже toнаписали partи 2.используют There,в we'llсвоих talkпроектах. aboutНаконец, writingинструмент ourStack
. ownОн codeдобавляет files,еще andодин writingслой moreповерх complicatedCabal functionsи withделает someего advancedпроще syntax.для Makeскачивания sureпакетов, youс alsoкоторыми don'tне missхотелось partбы 3,иметь whereконфликтов. we'llЕсли lookхотите atболее howдетальное easyрассмотрение itэтой isтемы, toможно createвзглянуть ourна ownStack
!data types!Mini-Course
ThisЧтобы seriesпроверить. alsoчто comesу withвас aвсе companionработает Githubправильно, repository!нужно Thisзапустить repositoryкоманду willghci
allowв youвашем toтерминале workи withдождаться someзапуска ofинтепретатора. theМы codeпроведем samplesостаток fromэтой theseлекции articles.в InGHCI thisпытая firstнекоторые partбазовые though,свойства we'll mostly be working within GHCI, rather than source code files.языка.
Finally, once you're done with these, make sure to check out our Beginner's Checklist! This will provide you with a review of the series and guide you to many more resources for improving your Haskell!
Выражения
InstallationУ Ifвас youуже haveвсе notустановленно, touchedдавайте anyпойдем дальше! Самое фундаментальное в Haskell whatsoever,- theвсё firstчто stepпишется isэто toвыражение. downloadВсе theпрограммы Haskellсостоят Platform.из Downloadвычисления theэтих latestвыражений. versionДавайте forначнем yourс operatingпроверки system from and follow the on-screen prompts.
The platform currently includes 4 main items. First, it has GHC, the most commonly used Haskell compiler. The compiler is what takes your code and turns it into something the computer can run. Second, it has GHCI, an interpreter for the Haskell language. It allows you to enter expressions and test some calculations without having to put your code in a separate file.
Third, it includes "Cabal"некоторых, aсамых dependencyпростых managerвыражений, forкоторые Haskellмы libraries.можем Thisсделать. willВеедите allowследующее youвыражение toв downloadинтерпретатор. codeКаждый thatраз otherпри peopleнажатии haveenter
, writtenинтерпретатор andдолжен useпросто itвыводить inобратно yourто, ownчто projects.вы Finally, there is the "Stack" tool. This adds another layer on top of Cabal and makes it easier for you to download packages that won't cause conflicts. If you want more details about Stack, you can look at our Stack Mini-Course!
To test if you've installed everything correctly, see if the command ghci works in your terminal and brings up the interpreter. We'll spend the rest of this lesson in GHCI trying out some of Haskell's basic language features.
Expressions
Now that you've installed everything, let's get down to the basics! The most fundamental thing about Haskell is that everything you write is an expression. All your program actually consists of is evaluating these expressions. Let's start by examining some of the most basic expressions we can make. Try entering the following 6 expressions in the interpreter. When you press enter each time, the interpreter should simply echo back what you typed.ввели.
>> True
True
>> False
False
>> 5
5
>> 5.5
5.5
>> 'a'
'a'
>> "Hello"
"Hello
WithЭтим thisнабором seriesвыражений, ofмы expressions,покрыли weбольшую coverчасть mostбазовых ofтипов theязыка. basicЕсли "types"вы ofделали theпрограммы language.ранее, Ifэти you'veбазовые doneтипы anyдолжны programmingбыть inвам theхорошо past,знакомы. theseПервыве basicдва typesвыражения should- beбулевы. prettyTrue
familiarи toFalse
you.- Theединственные firstзначения twoэтого areтипа. "boolean"Мы expressions.так Trueже andможем Falseделать areвыражения actuallyиз theчисел, onlyцелых valuesи ofдесятичных. thisНаконец, type.мы Weможем canделать alsoвыражения makeотображающием expressionsотдельные outсимволы ofтак numbers,же bothкак wholeи andцелые decimal.слова, Finally,которые weмы canназовем make expressions representing individual characters as well as whole words, which we call strings.string
.
InВ theинтерпретаторе, interpreter,мы weможем canназначить assignвыражения expressionsдля toнаименования namesиспользуя bylet
usingи "let"знак andравно. anЭто equalsсохранит sign.выражение Thisпод savesименем theк expressionкоторому underмы thatможем nameссылаться so we can refer to it later.позже.
>> let firstString = "Hello"
>> firstString
"Hello"
TypesТипа
Now,Теперь, oneодно ofиз theклассных coolвещей things aboutо Haskell isэто thatто, everyчто expressionлюбое hasвыражение aимеет type.тип. Let'sДавайте examineпроверим theтип typesбазового ofвыражения theкоторое basicмы expressionsввели weвышее. enteredМы above.увидим, We'llчто seeидея thatо theкоторой ideasмы weговорим wereформализованна talkingи aboutсамом areязыке. formalizedВы inможете theпосмотреть languageтип itself.любого Youвыражения canиспользуя examineкоманду the type of any expression by using the :t
.command:commang
>> :t True
True :: Bool
>> :t False
False :: Bool
>> :t 5
5 :: Num t => t
>> :t 5.5
5.5 :: Fractional t => t
>> :t 'a'
'a' :: Char
>> :t "Hello"
"Hello" :: [Char]
AПара coupleвыражений ofпроста, theseно areдругая straightforward,пара butкажется aстранной. coupleПоследнее areвыражение kind'veэто weird.же Isn'tпросто thatстрока? lastВерно. oneВы justможете aиспользовать String?понятие WellString
yes.в Youвашем canкоде. useНо theпод term String in your code. Under the hood though,капотом, Haskell thinksдумает ofо Stringsстроках asкак aо listсписке ofсимволов, characters,о whichчем isговорит what [Char]
. means.Мы We'llвернемся getк toэтому thatпозже. later.True
Trueи andFalse
Falseотвечает correspondза toтип theBool
, Boolкак type,мы justи likeожидаем. we'dСимвол expect.a
Theпросто 'a'единичный characterChar
. isНаши aчисла singleнемного Char.сложнее. OurВременно numbersигнорируем areслова aNum
littleи moreFractional
. complicated.Это Ignoreто theкак Numмы andможем Fractionalссылаться wordsна forразличные now.типы. ThisМы isбудем howпредставлять weсебе canцелые referчисла toв aкачестве wholeInt
rangeтипа, ofа types.с We'llплавающей thinkзапятой ofкак wholeDouble
. numbersМы asможем havingявно typeназначить Int, and floating point numbers as having type Double. We can explicitly assign the type we like like so:тип:
>> let a = 5 :: Int
>> :t a
a :: Int
>> let b = 5.5 :: Double
>> :t b
b :: Double
WeМы canуже alreadyможем seeувидеть, somethingчто-то prettyочень coolинтересно aboutо Haskell. ItОн canможет inferвзаимодействовать someс informationинформацией aboutо theтипе typesнашего ofвыражения ourпросто expressionsисходя justиз fromформы. theirВ form.общем, Weнам generallyне don'tнужно needявно toдавать explicitlyтип giveдля aкаждого typeнашего toвыражения eachкак ofмы ourделали expressionsв like we do in a language likeязыках Java orили C+С+ (think int a = ...)+.
FunctionsФункции
Let'sДавайте startначнем doingделать someнекоторые computationsвычисления withс ourнашими newвыражениями expressionsи andувидим, seeчто whatбудет weпроисходить. comeМы upможем with.начать Weс canкоторых startбазовых withматематических some basic mathematical calculations:вычислений:
>> 4 + 5
9
>> 10 - 6
4
>> 3 * 5
15
>> 3.3 * 4
13.2
>> (3.3 :: Double) * (4 :: Int)
OnceВ again,то we'veвремя, beenкак thrownмы aзакончили curveballс byэтой thisчастью, lastмы example!поняли Byчто theздесь timeпроисходит we'reи doneкак withмы thisможем section,это we'llисправить. understandТеперь, what'sважная goingзаметка, onвсё here and how we can fix it. Now, an important note here is that we said everything inв Haskell is- anвыражение, expression,и andлюбое everyвыржаение expressionимеет hasсвой aтип. type.Логично, Soмы logically,должны weуметь shouldузнавать beи ableопределять toтипа askэтих andразличных determineвыражений. theИ typesмы ofопределенно theseможем differentэто expressions.делать. AndНам weнужно certainlyпросто can!обернуть Weв justскобки. haveчтобы toубедиться, putчто parenthesesтип aroundкоманды themзнал, toчто makeнужно sureвключить theвыражение type command knows to include the whole expression.целиком.
>> let a = 4 :: Int
>> let b = 5 :: Int
>> a + b
9
>> :t (a + b)
(a + b) :: Int
ThisОператор makes+
, perfectдаже sense,сам sinceпо theсебе result,без 9,числе, seemsвсё likeеще anвыражение! IntЭто asнаш well.первй Butпример thisфункции, isили whereвыражения itкоторое getsпринимает cool.аргументы. TheКогда +мы operator,обращаемся evenк onнему itsсамому ownто withoutего theнужно numbers,обернуть isв still an expression! It is our first example of a function, or an expression that takes arguments. When we represent it by itself, we put parentheses around it.скобки.
>> :t (+)
(+) :: Num a => a -> a -> a
ThisЭто isнаш ourпервый firstпример exampleотражения ofтипа representingфункции. theВажная typeчасть ofтут a- function. The important part is a -> a ->
. a.aThisЭто tellsвыражение usговорит thatнам что (+)
isэто aфункция functionкоторая takingпринимает twoдва arguments,аргумента, whichкоторые mustдожны haveиметь theодин sameи type.тот Itже willти. thenИ giveзатем usвыдает aнам resultрезультат thatтого hasже theтипа, sameчто typeи asвходные theданные. inputs.Num
Theуказывает, Numчто aнам portionнужно specifiesиспользовать weчисловые haveтипы, toвроде useцелых numericи types,с likeплавающей intsзапятой. andМы floats.не Weможем can'tнапример forсделать instance do:так:
>> "Hello " + "World"
ButНо thisесть explainsобъяснение whyтому, weпочему wereнельзя notсложить ableнапрмер toInt
addи anDouble
intвместе. andФункция aтребует doubleиспользовать together.одинаковый Theтип functionдля demandsобоих weаргументов. useЧтобы theэто sameисправить, typeнам forнужно bothиспользовать arguments.другую Toфункци fixдля this,того. we'dчтобы haveизменить toтип useодного aиз differentаргумента, functionчтобы toон changeсовпадал theс typeдругим. ofИли oneмы ofможем themпозволить toвзаимодействию matchтипов theразрешить other.это Orсамому, weкак couldмы letделали typeэто inferenceв figureпримере itвыше. out,Но likeмы weбежим hadвперед inпоезда. theДавайте exampleостановимся rightна aboveсмысле it.того Butкак we're getting ahead of ourselves. Let's focus on the semantics of how weмы "apply"применяем" thisэти function.функции.
InВ general,общем, weмы "apply"применяем" functionsфункции byпомещая placingаргумент theпосле argumentsфункции. afterФнукция the(+)
functions.специальная, Theтак +как functionмы isможем specialиспользовать inеё thatмежду weаргументами. canЕсли useмы itвсё asтаки anхотим, operatorто inможем betweenиспользовать itsскобки arguments.вокруг Ifнее weи wantпоставим though,как weобычную canфункцию useвначале. parenthesesВ aroundэтом itслучае andоба treatаргумента itбудут likeи aстоять normal function. In this case, both arguments need to come after it:после.
>> (+) 4 5
9
What'sЧто reallyважно coolзнать aboutпро functionsфункции, isто thatчто weне don'tобязательно haveиспользовать toсразу applyвсе all the arguments at once!аргументы. WeМы canможем takeвзять theтот sameже additionоператор operatorсложения andи applyприменит itтолько toодно onlyчисло. oneЭто ofназывается theчастичное numbers. This is called partial application.применние.
>> let a = 4 :: Int
>> :t (a +)
(a +) :: Int -> Int
OnСам itsпо own,себе (+)
wasоператор anкоторы operatorпринимает that2 couldаргумента. takeСейчас twoмы arguments.к Nowнему we'veприменили appliedодин oneаргумент, argumentкоторый (anпринимает Int)оставшийся. toДальше, it.так Theкак resultingодин expressionаргумент isбыл nowInt
aвторой functionтоже thatдолжен takesбыть oneInt
. remainingМы argument.можем Furthermore,использовать sinceчастичное theприменение firstдля argumentвыражения weиспользуя passedlet
wasи anзатем Int,применить theвторой second one must also be an Int. We can assign our partial function to an expression using let and then apply it to a second argument!аргумент.
>> let f = (4 +)
>> f 5
9
Let's experiment a bit with some more operators, this time on boolean values. These are important because they'll let you build more advanced conditions when you start writing functions. There are three main operators, which work the way you would expect for other languages: And, Or, and Not. The first two take two boolean parameters and return a boolean, and the last takes a single boolean and returns it.
>> :t (&&)
(&&) :: Bool -> Bool -> Bool
>> :t (||)
(||) :: Bool -> Bool -> Bool
>> :t not
not :: Bool -> Bool
And we can see some simple examples of their behavior:
>> True && False
False
>> True && True
True
>> False || True
True
>> not True
False
One final function we'll want to know about is the equality function. This can take two arguments of almost any type and determine if they are equal.
>> 5 == 5
True
>> 4.3 == 4.8
False
>> True == False
False
>> "Hello" == "Hello"
True
Lists
Now we're going to broaden our horizons a bit and discuss some more advanced types. The first concept we'll look at is lists. These are a series of values that have the same type. We denote lists by using square brackets. A list can have zero elements, and we call this the empty list.
>> :t [1,2,3,4,7]
[1,2,3,4,7] :: Num t -> [t]
>> :t [True, False, True]
[True, False, True] :: [Bool]
>> :t ["Hello", True]
Error! (these aren't the same type!)
>> :t []
[] :: [t]
Notice the error in the third example! Lists can't have different types of elements! Remember we said earlier that a string is really just a list of characters. We can test how this looks:
>> "Hello" == ['H', 'e', 'l', 'l', 'o']
True
Lists can be combined using the (++) operator. Because strings are lists, this allows you to combine strings like you can in other languages.
>> [1,2,3] ++ [4,5,6]
[1,2,3,4,5,6]
>> "Hello " ++ "World"
"Hello World"
Lists also have two functions that are specially designed to get certain elements out. We can use the head function to get the first element of the list. Similarly, we can use the tail function to get all the elements of a list EXCEPT the head element.
>> head [1,2,3]
1
>> tail [True, False, False]
[False, False]
>> tail [3]
[]
Beware though! Calling either of these functions on an empty list will result in an error!
>> head []
Error!
>> tail []
Error!
Tuples
So now that we know about lists, you might be wondering if there's any way we can combine elements that do not have the same type. In fact there is! These are called tuples! You can make tuples that have any number of elements, each with different types. Tuples are denoted using parentheses:
>> :t (1 :: Int, "Hello", True)
(1 :: Int, "Hello", True) :: (Int, [Char], Bool)
>> :t (1 :: Int, 2 :: Int)
(1 :: Int, 2 :: Int) :: (Int, Int)
Each tuple we make has its own type based on the constituent types within the tuple. This means the following are all different types, even though some of them share the same types of elements, or have the same length:
>> :t (1 :: Int, 2 :: Int)
(1 :: Int, 2 :: Int) :: (Int, Int)
>> :t (2 :: Int, 3 :: Int, 4 :: Int)
(2 :: Int, 3 :: Int, 4 :: Int) :: (Int, Int, Int)
>> :t ("Hi", "Bye", "Good")
([Char], [Char], [Char])
Since tuples are expressions like any other, we can make lists out of them! However, we cannot combine tuples of different types in the same list.
>> :t [(1 :: Int, 2 :: Int), (3 :: Int, 4 :: Int)]
[(1 :: Int, 2 :: Int), (3 :: Int, 4 :: Int)] :: [(Int, Int)]
>> :t [(True, False, True), (False, False, False)]
[(Bool, Bool, Bool)]
>> :t [(1,2), (1,2,3)]
Error
Conclusion
This concludes the first section of our liftoff series. Look at how much we've gone through in one article! We installed the Haskell platform and started experimenting with the GHCI, a code interpreter. We also learned about expressions, types, and functions, which are the building blocks of Haskell.
In part 2 of this series, we'll start writing our own code in Haskell source files and learn more about the language syntax. We'll examine how we can print output to a user of our program, as well as get their input. We'll also start writing our own functions and look at the various ways we have to specify function behavior.
Then in part 3, we'll start building our own data types. We'll see how simple Haskell's algebraic data types are, and how type synonyms and newtypes can give us additional control over the style of our code.
If you want some more resources for learning introductory Haskell, check out our Beginner's Checklist! It'll tell you about some cool tools and other great beginner resources! You'll also get a great summary of the key points of this Liftoff series!
And if you're itching to do some more advanced Haskell work, be sure to check out our Stack Mini Course! This will walk you through creating a basic Haskell program with the Stack utility! You'll be able to seamlessly incorporate code libraries from online and build your own applications piece by piece!