├── .gitignore ├── .vscode ├── settings.json └── tasks.json ├── Lab1 ├── CHANGELOG.md ├── LICENSE ├── Lab1.cabal ├── README.md ├── Setup.hs ├── package.yaml ├── src │ ├── FirstSteps.hs │ ├── Lists.hs │ └── Luhn.hs ├── stack.yaml ├── stack.yaml.lock └── test │ └── Spec.hs ├── Lab2 ├── CHANGELOG.md ├── LICENSE ├── Lab2.cabal ├── README.md ├── Setup.hs ├── package.yaml ├── src │ ├── Poly.hs │ └── SimpleLang.hs ├── stack.yaml ├── stack.yaml.lock └── test │ └── Spec.hs ├── Lab3 ├── CHANGELOG.md ├── LICENSE ├── Lab3.cabal ├── README.md ├── Setup.hs ├── package.yaml ├── src │ ├── FunctorsMonads.hs │ └── Streams.hs ├── stack.yaml ├── stack.yaml.lock └── test │ └── Spec.hs ├── README.md ├── Typeclassopedia-diagram.png ├── concurrency_eval1.png ├── concurrency_eval2.png ├── latexindent.yaml ├── lecture1.pdf ├── lecture1.tex ├── lecture10_monads.pdf ├── lecture10_monads.tex ├── lecture11_io.pdf ├── lecture11_io.tex ├── lecture12_property_testing.pdf ├── lecture12_property_testing.tex ├── lecture13_concurrency.pdf ├── lecture13_concurrency.tex ├── lecture14_final.pdf ├── lecture14_final.tex ├── lecture1_programming_paradigms.eps ├── lecture1_programming_paradigms.eps_tex ├── lecture1_programming_paradigms.svg ├── lecture2.pdf ├── lecture2.tex ├── lecture3.pdf ├── lecture3.tex ├── lecture4.pdf ├── lecture4.tex ├── lecture5_hof.pdf ├── lecture5_hof.tex ├── lecture6_list_hof.pdf ├── lecture6_list_hof.tex ├── lecture7_type_inference.pdf ├── lecture7_type_inference.tex ├── lecture8_laziness.pdf ├── lecture8_laziness.tex ├── lecture9_higher_kinded_types.pdf ├── lecture9_higher_kinded_types.tex ├── lecture_preamble.tex ├── my_style.py └── paradigmsDIAGRAMeng108.pdf /.gitignore: -------------------------------------------------------------------------------- 1 | temp_* 2 | 3 | ## Core latex/pdflatex auxiliary files: 4 | *.aux 5 | *.lof 6 | *.log 7 | *.lot 8 | *.fls 9 | *.out 10 | *.toc 11 | *.fmt 12 | *.fot 13 | *.cb 14 | *.cb2 15 | .*.lb 16 | 17 | ## Intermediate documents: 18 | *.dvi 19 | *.xdv 20 | *-converted-to.* 21 | # these rules might exclude image files for figures etc. 22 | # *.ps 23 | # *.eps 24 | # *.pdf 25 | 26 | ## Generated if empty string is given at "Please type another file name for output:" 27 | .pdf 28 | 29 | ## Bibliography auxiliary files (bibtex/biblatex/biber): 30 | *.bbl 31 | *.bcf 32 | *.blg 33 | *-blx.aux 34 | *-blx.bib 35 | *.run.xml 36 | 37 | ## Build tool auxiliary files: 38 | *.fdb_latexmk 39 | *.synctex 40 | *.synctex(busy) 41 | *.synctex.gz 42 | *.synctex.gz(busy) 43 | *.pdfsync 44 | 45 | ## Auxiliary and intermediate files from other packages: 46 | # algorithms 47 | *.alg 48 | *.loa 49 | 50 | # achemso 51 | acs-*.bib 52 | 53 | # amsthm 54 | *.thm 55 | 56 | # beamer 57 | *.nav 58 | *.pre 59 | *.snm 60 | *.vrb 61 | 62 | # changes 63 | *.soc 64 | 65 | # cprotect 66 | *.cpt 67 | 68 | # elsarticle (documentclass of Elsevier journals) 69 | *.spl 70 | 71 | # endnotes 72 | *.ent 73 | 74 | # fixme 75 | *.lox 76 | 77 | # feynmf/feynmp 78 | *.mf 79 | *.mp 80 | *.t[1-9] 81 | *.t[1-9][0-9] 82 | *.tfm 83 | 84 | #(r)(e)ledmac/(r)(e)ledpar 85 | *.end 86 | *.?end 87 | *.[1-9] 88 | *.[1-9][0-9] 89 | *.[1-9][0-9][0-9] 90 | *.[1-9]R 91 | *.[1-9][0-9]R 92 | *.[1-9][0-9][0-9]R 93 | *.eledsec[1-9] 94 | *.eledsec[1-9]R 95 | *.eledsec[1-9][0-9] 96 | *.eledsec[1-9][0-9]R 97 | *.eledsec[1-9][0-9][0-9] 98 | *.eledsec[1-9][0-9][0-9]R 99 | 100 | # glossaries 101 | *.acn 102 | *.acr 103 | *.glg 104 | *.glo 105 | *.gls 106 | *.glsdefs 107 | 108 | # gnuplottex 109 | *-gnuplottex-* 110 | 111 | # gregoriotex 112 | *.gaux 113 | *.gtex 114 | 115 | # hyperref 116 | *.brf 117 | 118 | # knitr 119 | *-concordance.tex 120 | # TODO Comment the next line if you want to keep your tikz graphics files 121 | *.tikz 122 | *-tikzDictionary 123 | 124 | # listings 125 | *.lol 126 | 127 | # makeidx 128 | *.idx 129 | *.ilg 130 | *.ind 131 | *.ist 132 | 133 | # minitoc 134 | *.maf 135 | *.mlf 136 | *.mlt 137 | *.mtc[0-9]* 138 | *.slf[0-9]* 139 | *.slt[0-9]* 140 | *.stc[0-9]* 141 | 142 | # minted 143 | _minted* 144 | *.pyg 145 | 146 | # morewrites 147 | *.mw 148 | 149 | # nomencl 150 | *.nlo 151 | 152 | # pax 153 | *.pax 154 | 155 | # pdfpcnotes 156 | *.pdfpc 157 | 158 | # sagetex 159 | *.sagetex.sage 160 | *.sagetex.py 161 | *.sagetex.scmd 162 | 163 | # scrwfile 164 | *.wrt 165 | 166 | # sympy 167 | *.sout 168 | *.sympy 169 | sympy-plots-for-*.tex/ 170 | 171 | # pdfcomment 172 | *.upa 173 | *.upb 174 | 175 | # pythontex 176 | *.pytxcode 177 | pythontex-files-*/ 178 | 179 | # thmtools 180 | *.loe 181 | 182 | # TikZ & PGF 183 | *.dpth 184 | *.md5 185 | *.auxlock 186 | 187 | # todonotes 188 | *.tdo 189 | 190 | # easy-todo 191 | *.lod 192 | 193 | # xindy 194 | *.xdy 195 | 196 | # xypic precompiled matrices 197 | *.xyc 198 | 199 | # endfloat 200 | *.ttt 201 | *.fff 202 | 203 | # Latexian 204 | TSWLatexianTemp* 205 | 206 | ## Editors: 207 | # WinEdt 208 | *.bak 209 | *.sav 210 | 211 | # Texpad 212 | .texpadtmp 213 | 214 | # Kile 215 | *.backup 216 | 217 | # KBibTeX 218 | *~[0-9]* 219 | 220 | # auto folder when using emacs and auctex 221 | ./auto/* 222 | *.el 223 | 224 | # expex forward references with \gathertags 225 | *-tags.tex 226 | 227 | # standalone packages 228 | *.sta 229 | 230 | # Haskell 231 | dist 232 | dist-* 233 | cabal-dev 234 | *.o 235 | *.hi 236 | *.hie 237 | *.chi 238 | *.chs.h 239 | *.dyn_o 240 | *.dyn_hi 241 | .hpc 242 | .hsenv 243 | .cabal-sandbox/ 244 | cabal.sandbox.config 245 | *.prof 246 | *.aux 247 | *.hp 248 | *.eventlog 249 | .stack-work/ 250 | cabal.project.local 251 | cabal.project.local~ 252 | .HTF/ 253 | .ghc.environment.* 254 | 255 | # Backup files 256 | *~ 257 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "latex-workshop.latex.tools": [ 3 | { 4 | "name": "latexmk", 5 | "command": "latexmk", 6 | "args": [ 7 | "-shell-escape", 8 | "-synctex=1", 9 | "-interaction=nonstopmode", 10 | "-file-line-error", 11 | "-pdf", 12 | "-outdir=%OUTDIR%", 13 | "%DOC%" 14 | ], 15 | "env": {} 16 | }, 17 | { 18 | "name": "lualatexmk", 19 | "command": "latexmk", 20 | "args": [ 21 | "-shell-escape", 22 | "-synctex=1", 23 | "-interaction=nonstopmode", 24 | "-file-line-error", 25 | "-lualatex", 26 | "-outdir=%OUTDIR%", 27 | "%DOC%" 28 | ], 29 | "env": {} 30 | }, 31 | { 32 | "name": "xelatexmk", 33 | "command": "latexmk", 34 | "args": [ 35 | "-shell-escape", 36 | "-synctex=1", 37 | "-interaction=nonstopmode", 38 | "-file-line-error", 39 | "-xelatex", 40 | "-outdir=%OUTDIR%", 41 | "%DOC%" 42 | ], 43 | "env": {} 44 | }, 45 | { 46 | "name": "latexmk_rconly", 47 | "command": "latexmk", 48 | "args": [ 49 | "%DOC%" 50 | ], 51 | "env": {} 52 | }, 53 | { 54 | "name": "pdflatex", 55 | "command": "pdflatex", 56 | "args": [ 57 | "-shell-escape", 58 | "-synctex=1", 59 | "-interaction=nonstopmode", 60 | "-file-line-error", 61 | "%DOC%" 62 | ], 63 | "env": {} 64 | }, 65 | { 66 | "name": "bibtex", 67 | "command": "bibtex", 68 | "args": [ 69 | "%DOCFILE%" 70 | ], 71 | "env": {} 72 | }, 73 | { 74 | "name": "rnw2tex", 75 | "command": "Rscript", 76 | "args": [ 77 | "-e", 78 | "knitr::opts_knit$set(concordance = TRUE); knitr::knit('%DOCFILE_EXT%')" 79 | ], 80 | "env": {} 81 | }, 82 | { 83 | "name": "jnw2tex", 84 | "command": "julia", 85 | "args": [ 86 | "-e", 87 | "using Weave; weave(\"%DOC_EXT%\", doctype=\"tex\")" 88 | ], 89 | "env": {} 90 | }, 91 | { 92 | "name": "jnw2texminted", 93 | "command": "julia", 94 | "args": [ 95 | "-e", 96 | "using Weave; weave(\"%DOC_EXT%\", doctype=\"texminted\")" 97 | ], 98 | "env": {} 99 | }, 100 | { 101 | "name": "pnw2tex", 102 | "command": "pweave", 103 | "args": [ 104 | "-f", 105 | "tex", 106 | "%DOC_EXT%" 107 | ], 108 | "env": {} 109 | }, 110 | { 111 | "name": "pnw2texminted", 112 | "command": "pweave", 113 | "args": [ 114 | "-f", 115 | "texminted", 116 | "%DOC_EXT%" 117 | ], 118 | "env": {} 119 | }, 120 | { 121 | "name": "tectonic", 122 | "command": "tectonic", 123 | "args": [ 124 | "--synctex", 125 | "--keep-logs", 126 | "%DOC%.tex" 127 | ], 128 | "env": {} 129 | } 130 | ], 131 | "latex-workshop.latexindent.args": [ 132 | "-c", 133 | "%DIR%/", 134 | "%TMPFILE%", 135 | "-y=defaultIndent: '%INDENT%'", 136 | "-l" 137 | ], 138 | "todo-tree.tree.scanMode": "workspace" 139 | } 140 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | // Automatically created by phoityne-vscode extension. 4 | 5 | "version": "2.0.0", 6 | "presentation": { 7 | "reveal": "always", 8 | "panel": "new" 9 | }, 10 | "tasks": [ 11 | { 12 | // F7 13 | "group": { 14 | "kind": "build", 15 | "isDefault": true 16 | }, 17 | "label": "haskell build", 18 | "type": "shell", 19 | //"command": "cabal configure && cabal build" 20 | "command": "stack build" 21 | }, 22 | { 23 | // F6 24 | "group": "build", 25 | "type": "shell", 26 | "label": "haskell clean & build", 27 | //"command": "cabal clean && cabal configure && cabal build" 28 | "command": "stack clean && stack build" 29 | //"command": "stack clean ; stack build" // for powershell 30 | }, 31 | { 32 | // F8 33 | "group": { 34 | "kind": "test", 35 | "isDefault": true 36 | }, 37 | "type": "shell", 38 | "label": "haskell test", 39 | //"command": "cabal test" 40 | "command": "stack test" 41 | }, 42 | { 43 | // F6 44 | "isBackground": true, 45 | "type": "shell", 46 | "label": "haskell watch", 47 | "command": "stack build --test --no-run-tests --file-watch" 48 | } 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /Lab1/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog for `Lab1` 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to the 7 | [Haskell Package Versioning Policy](https://pvp.haskell.org/). 8 | 9 | ## Unreleased 10 | 11 | ## 0.1.0.0 - YYYY-MM-DD 12 | -------------------------------------------------------------------------------- /Lab1/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Author name here (c) 2023 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Author name here nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /Lab1/Lab1.cabal: -------------------------------------------------------------------------------- 1 | cabal-version: 1.12 2 | 3 | -- This file has been generated from package.yaml by hpack version 0.35.0. 4 | -- 5 | -- see: https://github.com/sol/hpack 6 | 7 | name: Lab1 8 | version: 0.1.0.0 9 | description: Please see the README on GitHub at 10 | homepage: https://github.com/githubuser/Lab1#readme 11 | bug-reports: https://github.com/githubuser/Lab1/issues 12 | author: Author name here 13 | maintainer: example@example.com 14 | copyright: 2023 Author name here 15 | license: BSD3 16 | license-file: LICENSE 17 | build-type: Simple 18 | extra-source-files: 19 | README.md 20 | CHANGELOG.md 21 | 22 | source-repository head 23 | type: git 24 | location: https://github.com/githubuser/Lab1 25 | 26 | library 27 | exposed-modules: 28 | FirstSteps 29 | Lists 30 | Luhn 31 | other-modules: 32 | Paths_Lab1 33 | hs-source-dirs: 34 | src 35 | ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -Wno-unused-matches 36 | build-depends: 37 | base >=4.7 && <5 38 | default-language: Haskell2010 39 | 40 | test-suite Lab1-test 41 | type: exitcode-stdio-1.0 42 | main-is: Spec.hs 43 | other-modules: 44 | Paths_Lab1 45 | hs-source-dirs: 46 | test 47 | ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -Wno-unused-matches -threaded -rtsopts -with-rtsopts=-N 48 | build-depends: 49 | Lab1 50 | , base >=4.7 && <5 51 | , hspec 52 | default-language: Haskell2010 53 | -------------------------------------------------------------------------------- /Lab1/README.md: -------------------------------------------------------------------------------- 1 | В пути к этой папке не используйте кириллицу. Если в комментариях видите крякозябры, откройте файл с кодировкой UTF-8. 2 | 3 | Вам нужно реализовать функции, определённые в файлах в директории `src`. Порядок: 4 | - FirstSteps.hs 5 | - Lists.hs 6 | - Luhn.hs 7 | Для сборки лабораторной используется [`stack`](https://docs.haskellstack.org/en/stable/#quick-start-guide). 8 | Он ставится вместе с [`ghcup`](https://www.haskell.org/ghcup/). 9 | Для тестов в `test/Spec.hs` используется библиотека [`HSpec`](https://hspec.github.io/). Она будет автоматически скачана и установлена при использовании приведённых ниже команд. 10 | 11 | Основные команды для этой лабораторной (остальные можно найти в документации): 12 | - Собрать проект: `stack build` 13 | - Открыть в REPL: `stack repl` 14 | - Запустить все тесты: `stack test` 15 | - Запустить один тест или группу по названию: `stack test --test-arguments "--match xor"` 16 | 17 | Рекомендуемый редактор: [Visual Studio Code](https://code.visualstudio.com/) с расширением [Haskell](https://marketplace.visualstudio.com/items?itemName=haskell.haskell), но можно использовать и другие. Примерное описание установки можно найти [здесь (на английском)](https://betterprogramming.pub/haskell-vs-code-setup-in-2021-6267cc991551). 18 | -------------------------------------------------------------------------------- /Lab1/Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /Lab1/package.yaml: -------------------------------------------------------------------------------- 1 | name: Lab1 2 | version: 0.1.0.0 3 | github: "githubuser/Lab1" 4 | license: BSD3 5 | author: "Author name here" 6 | maintainer: "example@example.com" 7 | copyright: "2023 Author name here" 8 | 9 | extra-source-files: 10 | - README.md 11 | - CHANGELOG.md 12 | 13 | # Metadata used when publishing your package 14 | # synopsis: Short description of your package 15 | # category: Web 16 | 17 | # To avoid duplicated efforts in documentation and dealing with the 18 | # complications of embedding Haddock markup inside cabal files, it is 19 | # common to point users to the README.md file. 20 | description: Please see the README on GitHub at 21 | 22 | dependencies: 23 | - base >= 4.7 && < 5 24 | 25 | ghc-options: 26 | - -Wall 27 | - -Wcompat 28 | - -Widentities 29 | - -Wincomplete-record-updates 30 | - -Wincomplete-uni-patterns 31 | # - -Wmissing-export-lists 32 | - -Wmissing-home-modules 33 | - -Wpartial-fields 34 | - -Wredundant-constraints 35 | - -Wno-unused-matches 36 | 37 | library: 38 | source-dirs: src 39 | 40 | tests: 41 | Lab1-test: 42 | main: Spec.hs 43 | source-dirs: test 44 | ghc-options: 45 | - -threaded 46 | - -rtsopts 47 | - -with-rtsopts=-N 48 | dependencies: 49 | - Lab1 50 | - hspec 51 | -------------------------------------------------------------------------------- /Lab1/src/FirstSteps.hs: -------------------------------------------------------------------------------- 1 | module FirstSteps 2 | where 3 | import Data.Word (Word8) 4 | 5 | -- xor x y находит "исключающее или" x и y 6 | -- xor True False == True 7 | -- xor True True == False 8 | 9 | -- используйте сопоставление с образцом 10 | xor :: Bool -> Bool -> Bool 11 | xor x y = error "todo" 12 | 13 | -- max3 x y z находит максимум из x, y и z 14 | -- max3 1 3 2 == 3 15 | -- max3 5 2 5 == 5 16 | -- median3 x y z находит второе по величине число (медиану) 17 | -- median3 1 3 2 == 2 18 | -- median3 5 2 5 == 5 19 | max3, median3 :: Integer -> Integer -> Integer -> Integer 20 | max3 x y z = error "todo" 21 | 22 | median3 x y z = error "todo" 23 | 24 | -- Типы данных, описывающие цвета в моделях 25 | -- RGB (https://ru.wikipedia.org/wiki/RGB), компоненты от 0 до 255 26 | -- и CMYK (https://ru.wikipedia.org/wiki/CMYK), компоненты от 0.0 до 1.0 27 | data RGB = RGB { red :: Word8, green :: Word8, blue :: Word8 } deriving (Eq, Show, Read) 28 | data CMYK = CMYK { cyan :: Double, magenta :: Double, yellow :: Double, black :: Double } deriving (Eq, Show, Read) 29 | -- Задайте функцию для их преобразования 30 | -- (формулы из http://www.codeproject.com/Articles/4488/XCmyk-CMYK-to-RGB-Calculator-with-source-code): 31 | -- Black = min(1-Red, 1-Green, 1-Blue) 32 | -- Cyan = (1-Red-Black) / (1-Black) 33 | -- Magenta = (1-Green-Black) / (1-Black) 34 | -- Yellow = (1-Blue-Black) / (1-Black) 35 | -- где значения Red, Green и Blue нормализованы от 0 до 1). 36 | 37 | -- Заметьте, что (/) для Int не работает, и неявного преобразования Int в Double нет. 38 | -- Это преобразование производится с помощью функции fromIntegral. 39 | rbgToCmyk :: RGB -> CMYK 40 | rbgToCmyk color = error "todo" 41 | 42 | -- geomProgression b q n находит n-й (считая с 0) член 43 | -- геометрической прогрессии, нулевой член которой -- b, 44 | -- а знаменатель -- q. 45 | -- geomProgression 3.0 2.0 2 == 12.0 46 | 47 | -- используйте рекурсию 48 | -- не забудьте случаи n < 0 и n == 0. 49 | geomProgression :: Double -> Double -> Integer -> Double 50 | geomProgression b q n = error "todo" 51 | 52 | -- coprime a b определяет, являются ли a и b взаимно простыми 53 | -- (определение: Целые числа называются взаимно простыми, 54 | -- если они не имеют никаких общих делителей, кроме +/-1) 55 | -- coprime 10 15 == False 56 | -- coprime 12 35 == True 57 | 58 | -- Используйте рекурсию 59 | -- Есть ли важные пограничные случаи или вспомогательные функции? Не забудьте добавить их в тесты. 60 | 61 | -- Полезные функции в Prelude (автоматически загруженной 62 | -- части стандартной библиотеки): quot, rem, quotRem 63 | -- (или div, mod, divMod в зависимости от того, как 64 | -- обрабатываете отрицательные числа) 65 | -- https://hackage.haskell.org/package/base-4.9.0.0/docs/Prelude.html 66 | coprime :: Integer -> Integer -> Bool 67 | coprime a b = error "todo" 68 | -------------------------------------------------------------------------------- /Lab1/src/Lists.hs: -------------------------------------------------------------------------------- 1 | module Lists where 2 | 3 | -- вектор задаётся списком координат 4 | newtype Point = Point [Double] deriving (Eq, Show, Read) 5 | 6 | -- distance x y находит расстояние между двумя точками в n-мерном 7 | -- пространстве. Если число координат точек разное, сообщите об ошибке. 8 | -- distance (Point [1.0, 0.0]) (Point [0.0, 1.0]) == sqrt 2.0 9 | -- distance (Point [0.0, 0.0]) (Point [0.0, 1.0]) == 1.0 10 | 11 | -- используйте рекурсию и сопоставление с образцом 12 | distance :: Point -> Point -> Double 13 | distance x y = error "todo" 14 | 15 | -- intersect xs ys возвращает список, содержащий общие элементы двух списков. 16 | -- intersect [1, 2, 4, 6] [5, 4, 2, 5, 7] == [2, 4] (или [4, 2]!) 17 | -- intersect [1, 2, 4, 6] [3, 5, 7] == [] 18 | 19 | -- используйте рекурсию и сопоставление с образцом 20 | intersect :: [Integer] -> [Integer] -> [Integer] 21 | intersect xs ys = error "todo" 22 | 23 | -- zipN принимает список списков и возвращает список, который состоит из 24 | -- списка их первых элементов, списка их вторых элементов, и так далее. 25 | -- zipN [[1, 2, 3], [4, 5, 6], [7, 8, 9]] == [[1, 4, 7], [2, 5, 8], [3, 6, 9]] 26 | -- zipN [[1, 2, 3], [4, 5], [6]] == [[1, 4, 6], [2, 5], [3]] 27 | zipN :: [[a]] -> [[a]] 28 | zipN xss = error "todo" 29 | 30 | -- Нижеперечисленные функции можно реализовать или рекурсивно, или с помощью 31 | -- стандартных функций для работы со списками (map, filter и т.д.) 32 | -- Попробуйте оба подхода! Хотя бы одну функцию реализуйте обоими способами. 33 | 34 | -- Если в списке xs есть такие элементы x, для которых f x == True, то 35 | -- find f xs возвращает Just (первый x), а findLast f xs -- Just (последний x). 36 | -- Если таких нет, то обе функции возвращают Nothing 37 | -- find (> 0) [-1, 2, -3, 4] == Just 2 38 | -- findLast (> 0) [-1, 2, -3, 4] == Just 4 39 | -- find (> 0) [-1, -2, -3] == Nothing 40 | find, findLast :: (a -> Bool) -> [a] -> Maybe a 41 | find f xs = error "todo" 42 | findLast f xs = error "todo" 43 | 44 | -- mapFuncs принимает список функций fs и возвращает список результатов 45 | -- применения всех функций из fs к x. 46 | -- mapFuncs [\x -> x*x, (1 +), \x -> if even x then 1 else 0] 3 == [9, 4, 0] 47 | mapFuncs :: [a -> b] -> a -> [b] 48 | mapFuncs fs x = error "todo" 49 | 50 | -- satisfiesAll принимает список предикатов (функций, возвращающих Bool) preds 51 | -- и возвращает True, если все они выполняются (т.е. возвращают True) для x. 52 | -- Полезные стандартные функции: and, all. 53 | -- satisfiesAll [even, \x -> x rem 5 == 0] 10 == True 54 | -- satisfiesAll [] 4 == True (кстати, почему?) 55 | satisfiesAll :: [a -> Bool] -> a -> Bool 56 | satisfiesAll preds x = error "todo" 57 | 58 | -- Непустой список состоит из первого элемента (головы) 59 | -- и обычного списка остальных элементов 60 | -- Например, NEL 1 [2, 3] соотвествует списку [1, 2, 3], а NEL 1 [] -- списку [1]. 61 | data NEL a = NEL a [a] deriving (Eq, Show, Read) 62 | 63 | -- Запишите правильный тип (т.е. такой, чтобы функция имела результат для любых аргументов 64 | -- без вызовов error) и реализуйте функции на NEL, аналогичные tail, last и zip 65 | -- tailNel :: NEL a -> ??? 66 | -- lastNel :: NEL a -> ??? 67 | -- zipNel :: NEL a -> NEL b -> ??? 68 | -- listToNel :: [a] -> ??? 69 | -- nelToList :: NEL a -> ??? 70 | -------------------------------------------------------------------------------- /Lab1/src/Luhn.hs: -------------------------------------------------------------------------------- 1 | module Luhn where 2 | 3 | -- Проверка корректности номера банковской карты алгоритмом Луна https://ru.wikipedia.org/wiki/Алгоритм_Луна. 4 | -- Алгоритм: 5 | -- 1. Все цифры, стоящие на чётных местах (считая с конца), удваиваются. Если при этом получается число, большее 9, то из него вычитается 9. Цифры, стояшие на нечётных местах, не изменяются. 6 | -- То есть: последняя цифра не меняется; предпоследнее удваивается; 3-е с конца (предпредпоследнее) не меняется; 4-е с конца удваивается и т.д. 7 | -- 2. Все полученные числа складываются. 8 | -- 3. Если полученная сумма кратна 10, то исходный список корректен. 9 | 10 | -- Не пытайтесь собрать всё в одну функцию, используйте вспомогательные. 11 | -- Например: разбить число на цифры (возможно, сразу в обратном порядке). 12 | -- Не забудьте добавить тесты, в том числе для вспомогательных функций! 13 | isLuhnValid :: Int -> Bool 14 | isLuhnValid = error "todo" 15 | -------------------------------------------------------------------------------- /Lab1/stack.yaml: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by 'stack init' 2 | # 3 | # Some commonly used options have been documented as comments in this file. 4 | # For advanced use and comprehensive documentation of the format, please see: 5 | # https://docs.haskellstack.org/en/stable/yaml_configuration/ 6 | 7 | # Resolver to choose a 'specific' stackage snapshot or a compiler version. 8 | # A snapshot resolver dictates the compiler version and the set of packages 9 | # to be used for project dependencies. For example: 10 | # 11 | # resolver: lts-3.5 12 | # resolver: nightly-2015-09-21 13 | # resolver: ghc-7.10.2 14 | # 15 | # The location of a snapshot can be provided as a file or url. Stack assumes 16 | # a snapshot provided as a file might change, whereas a url resource does not. 17 | # 18 | # resolver: ./custom-snapshot.yaml 19 | # resolver: https://example.com/snapshots/2018-01-01.yaml 20 | resolver: 21 | url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/20/11.yaml 22 | 23 | # User packages to be built. 24 | # Various formats can be used as shown in the example below. 25 | # 26 | # packages: 27 | # - some-directory 28 | # - https://example.com/foo/bar/baz-0.0.2.tar.gz 29 | # subdirs: 30 | # - auto-update 31 | # - wai 32 | packages: 33 | - . 34 | # Dependency packages to be pulled from upstream that are not in the resolver. 35 | # These entries can reference officially published versions as well as 36 | # forks / in-progress versions pinned to a git hash. For example: 37 | # 38 | # extra-deps: 39 | # - acme-missiles-0.3 40 | # - git: https://github.com/commercialhaskell/stack.git 41 | # commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a 42 | # 43 | # extra-deps: [] 44 | 45 | # Override default flag values for local packages and extra-deps 46 | # flags: {} 47 | 48 | # Extra package databases containing global packages 49 | # extra-package-dbs: [] 50 | 51 | # Control whether we use the GHC we find on the path 52 | system-ghc: true 53 | # 54 | # Require a specific version of stack, using version ranges 55 | # require-stack-version: -any # Default 56 | # require-stack-version: ">=2.9" 57 | # 58 | # Override the architecture used by stack, especially useful on Windows 59 | # arch: i386 60 | # arch: x86_64 61 | # 62 | # Extra directories used by stack for building 63 | # extra-include-dirs: [/path/to/dir] 64 | # extra-lib-dirs: [/path/to/dir] 65 | # 66 | # Allow a newer minor version of GHC than the snapshot specifies 67 | # compiler-check: newer-minor 68 | -------------------------------------------------------------------------------- /Lab1/stack.yaml.lock: -------------------------------------------------------------------------------- 1 | # This file was autogenerated by Stack. 2 | # You should not edit this file by hand. 3 | # For more information, please see the documentation at: 4 | # https://docs.haskellstack.org/en/stable/lock_files 5 | 6 | packages: [] 7 | snapshots: 8 | - completed: 9 | sha256: adbc602422dde10cc330175da7de8609e70afc41449a7e2d6e8b1827aa0e5008 10 | size: 649342 11 | url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/20/11.yaml 12 | original: 13 | url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/20/11.yaml 14 | -------------------------------------------------------------------------------- /Lab1/test/Spec.hs: -------------------------------------------------------------------------------- 1 | import FirstSteps 2 | import Lists 3 | import Luhn 4 | import Test.Hspec 5 | 6 | main :: IO () 7 | main = hspec $ do 8 | describe "first steps" $ do 9 | -- Можно вложить глубже: describe "xor" do $ ... чтобы дать названия отдельным тестам 10 | it "xor" $ do 11 | xor True True `shouldBe` False 12 | xor True False `shouldBe` True 13 | xor False True `shouldBe` True 14 | xor False False `shouldBe` False 15 | it "max3" $ do 16 | max3 1 3 2 `shouldBe` 3 17 | max3 5 2 5 `shouldBe` 5 18 | it "median3" pending 19 | it "rbgToCmyk" pending 20 | it "geomProgression" pending 21 | it "coprime" pending 22 | describe "lists" $ do 23 | it "distance" pending 24 | it "intersect" pending 25 | it "zipN" pending 26 | it "find" pending 27 | it "findLast" pending 28 | it "mapFuncs" pending 29 | it "tailNel" pending 30 | it "lastNel" pending 31 | it "zipNel" pending 32 | it "listToNel" pending 33 | it "nelToList" pending 34 | describe "luhn" $ it "" pending 35 | -------------------------------------------------------------------------------- /Lab2/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog for `Lab2` 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to the 7 | [Haskell Package Versioning Policy](https://pvp.haskell.org/). 8 | 9 | ## Unreleased 10 | 11 | ## 0.1.0.0 - YYYY-MM-DD 12 | -------------------------------------------------------------------------------- /Lab2/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Author name here (c) 2023 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Author name here nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /Lab2/Lab2.cabal: -------------------------------------------------------------------------------- 1 | cabal-version: 1.12 2 | 3 | -- This file has been generated from package.yaml by hpack version 0.35.0. 4 | -- 5 | -- see: https://github.com/sol/hpack 6 | 7 | name: Lab2 8 | version: 0.1.0.0 9 | description: Please see the README on GitHub at 10 | homepage: https://github.com/githubuser/Lab2#readme 11 | bug-reports: https://github.com/githubuser/Lab2/issues 12 | author: Author name here 13 | maintainer: example@example.com 14 | copyright: 2023 Author name here 15 | license: BSD3 16 | license-file: LICENSE 17 | build-type: Simple 18 | extra-source-files: 19 | README.md 20 | CHANGELOG.md 21 | 22 | source-repository head 23 | type: git 24 | location: https://github.com/githubuser/Lab2 25 | 26 | library 27 | exposed-modules: 28 | Poly 29 | SimpleLang 30 | other-modules: 31 | Paths_Lab2 32 | hs-source-dirs: 33 | src 34 | ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -Wno-unused-matches 35 | build-depends: 36 | base >=4.7 && <5 37 | default-language: Haskell2010 38 | 39 | test-suite Lab2-test 40 | type: exitcode-stdio-1.0 41 | main-is: Spec.hs 42 | other-modules: 43 | Paths_Lab2 44 | hs-source-dirs: 45 | test 46 | ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -Wno-unused-matches -threaded -rtsopts -with-rtsopts=-N 47 | build-depends: 48 | Lab2 49 | , base >=4.7 && <5 50 | , hspec 51 | default-language: Haskell2010 52 | -------------------------------------------------------------------------------- /Lab2/README.md: -------------------------------------------------------------------------------- 1 | Вам нужно реализовать функции, определённые в файлах в директории `src`. Порядок: 2 | - Poly.hs 3 | - SimpleLang.hs 4 | Остальное смотрите в Lab1. 5 | -------------------------------------------------------------------------------- /Lab2/Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /Lab2/package.yaml: -------------------------------------------------------------------------------- 1 | name: Lab2 2 | version: 0.1.0.0 3 | github: "githubuser/Lab2" 4 | license: BSD3 5 | author: "Author name here" 6 | maintainer: "example@example.com" 7 | copyright: "2023 Author name here" 8 | 9 | extra-source-files: 10 | - README.md 11 | - CHANGELOG.md 12 | 13 | # Metadata used when publishing your package 14 | # synopsis: Short description of your package 15 | # category: Web 16 | 17 | # To avoid duplicated efforts in documentation and dealing with the 18 | # complications of embedding Haddock markup inside cabal files, it is 19 | # common to point users to the README.md file. 20 | description: Please see the README on GitHub at 21 | 22 | dependencies: 23 | - base >= 4.7 && < 5 24 | 25 | ghc-options: 26 | - -Wall 27 | - -Wcompat 28 | - -Widentities 29 | - -Wincomplete-record-updates 30 | - -Wincomplete-uni-patterns 31 | # - -Wmissing-export-lists 32 | - -Wmissing-home-modules 33 | - -Wpartial-fields 34 | - -Wredundant-constraints 35 | - -Wno-unused-matches 36 | 37 | library: 38 | source-dirs: src 39 | 40 | tests: 41 | Lab2-test: 42 | main: Spec.hs 43 | source-dirs: test 44 | ghc-options: 45 | - -threaded 46 | - -rtsopts 47 | - -with-rtsopts=-N 48 | dependencies: 49 | - Lab2 50 | - hspec 51 | -------------------------------------------------------------------------------- /Lab2/src/Poly.hs: -------------------------------------------------------------------------------- 1 | -- Не забудьте добавить тесты. 2 | 3 | module Poly where 4 | 5 | -- Многочлены 6 | -- a -- тип коэффициентов, список начинается со свободного члена. 7 | -- Бонус: при решении следующих заданий подумайте, какие стали бы проще или 8 | -- сложнее при обратном порядке коэффициентов (и добавьте комментарий). 9 | newtype Poly a = P [a] 10 | 11 | -- Задание 1 ----------------------------------------- 12 | 13 | -- Определите многочлен $x$. 14 | x :: Num a => Poly a 15 | x = undefined 16 | 17 | -- Задание 2 ----------------------------------------- 18 | 19 | -- Функция, считающая значение многочлена в точке 20 | applyPoly :: Num a => Poly a -> a -> a 21 | applyPoly = undefined 22 | 23 | -- Задание 3 ---------------------------------------- 24 | 25 | -- Определите равенство многочленов 26 | -- Заметьте, что многочлены с разными списками коэффициентов 27 | -- могут быть равны! Подумайте, почему. 28 | instance (Num a, Eq a) => Eq (Poly a) where 29 | (==) = undefined 30 | 31 | -- Задание 4 ----------------------------------------- 32 | 33 | -- Определите перевод многочлена в строку. 34 | -- Это должна быть стандартная математическая запись, 35 | -- например: show (3 * x * x + 1) == "3 * x^2 + 1"). 36 | -- (* и + для многочленов можно будет использовать после задания 6.) 37 | instance (Num a, Eq a, Show a) => Show (Poly a) where 38 | show = undefined 39 | 40 | -- Задание 5 ----------------------------------------- 41 | 42 | -- Определите сложение многочленов 43 | plus :: Num a => Poly a -> Poly a -> Poly a 44 | plus = undefined 45 | 46 | -- Задание 6 ----------------------------------------- 47 | 48 | -- Определите умножение многочленов 49 | times :: Num a => Poly a -> Poly a -> Poly a 50 | times = undefined 51 | 52 | -- Задание 7 ----------------------------------------- 53 | 54 | -- Сделайте многочлены числовым типом 55 | instance Num a => Num (Poly a) where 56 | (+) = plus 57 | (*) = times 58 | negate = undefined 59 | fromInteger = undefined 60 | -- Эти функции оставить как undefined, поскольку для 61 | -- многочленов они не имеют математического смысла 62 | abs = undefined 63 | signum = undefined 64 | 65 | -- Задание 8 ----------------------------------------- 66 | 67 | -- Реализуйте nderiv через deriv 68 | class Num a => Differentiable a where 69 | -- взятие производной 70 | deriv :: a -> a 71 | -- взятие n-ной производной 72 | nderiv :: Int -> a -> a 73 | nderiv = undefined 74 | 75 | -- Задание 9 ----------------------------------------- 76 | 77 | -- Определите экземпляр класса типов 78 | instance Num a => Differentiable (Poly a) where 79 | deriv = undefined 80 | -------------------------------------------------------------------------------- /Lab2/src/SimpleLang.hs: -------------------------------------------------------------------------------- 1 | module SimpleLang where 2 | -- Язык Simple -- очень простой императивный язык. 3 | -- В нём только один тип данных: целые числа. 4 | 5 | data Expression = 6 | Var String -- Переменные 7 | | Val Int -- Целые константы 8 | | Op Expression Bop Expression -- Бинарные операции 9 | deriving (Show, Eq) 10 | 11 | data Bop = 12 | Plus 13 | | Minus 14 | | Times 15 | | Divide 16 | | Gt -- > 17 | | Ge -- >= 18 | | Lt -- < 19 | | Le -- <= 20 | | Eql -- == 21 | deriving (Show, Eq) 22 | 23 | data Statement = 24 | -- присвоить переменной значение выражения 25 | Assign String Expression 26 | -- увеличить переменную на единицу 27 | | Incr String 28 | -- ненулевые значения работают как истина в if, while и for 29 | | If Expression Statement Statement 30 | | While Expression Statement 31 | | For Statement Expression Statement Statement 32 | -- как { ... } в C-подобных языках 33 | | Block [Statement] 34 | -- пустая инструкция 35 | | Skip 36 | deriving (Show, Eq) 37 | 38 | -- примеры программ на этом языке в конце модуля 39 | 40 | -- по состоянию можно получить значение каждой переменной 41 | -- (в реальной программе скорее использовалось бы Data.Map.Map String Int) 42 | type State = String -> Int 43 | 44 | -- Задание 1 ----------------------------------------- 45 | 46 | -- в начальном состоянии все переменные имеют значение 0 47 | empty :: State 48 | empty = undefined 49 | 50 | -- возвращает состояние, в котором переменная var имеет значение newVal, 51 | -- все остальные -- то же, что в state 52 | extend :: State -> String -> Int -> State 53 | extend state var newVal = undefined 54 | 55 | -- Задание 2 ----------------------------------------- 56 | 57 | -- возвращает значение выражения expr при значениях переменных из state. 58 | eval :: State -> Expression -> Int 59 | eval state expr = undefined 60 | 61 | -- Задание 3 ----------------------------------------- 62 | 63 | -- Можно выразить Incr через Assign, For через While, Block через 64 | -- последовательное выполнение двух инструкций (; в C). 65 | -- Следующий тип задаёт упрощённый набор инструкций (промежуточный язык Simpler). 66 | data DietStatement = DAssign String Expression 67 | | DIf Expression DietStatement DietStatement 68 | | DWhile Expression DietStatement 69 | | DSequence DietStatement DietStatement 70 | | DSkip 71 | deriving (Show, Eq) 72 | 73 | -- упрощает программу Simple 74 | desugar :: Statement -> DietStatement 75 | desugar = undefined 76 | 77 | -- Задание 4 ----------------------------------------- 78 | 79 | -- принимает начальное состояние и программу Simpler 80 | -- и возвращает состояние после работы программы 81 | runSimpler :: State -> DietStatement -> State 82 | runSimpler = undefined 83 | 84 | -- 85 | -- in s "A" ~?= 10 86 | 87 | -- принимает начальное состояние и программу Simple 88 | -- и возвращает состояние после работы программы 89 | run :: State -> Statement -> State 90 | run = undefined 91 | 92 | -- Программы ------------------------------------------- 93 | 94 | {- Вычисление факториала 95 | 96 | for (Out := 1; In > 0; In := In - 1) { 97 | Out := In * Out 98 | } 99 | -} 100 | factorial :: Statement 101 | factorial = For (Assign "Out" (Val 1)) 102 | (Op (Var "In") Gt (Val 0)) 103 | (Assign "In" (Op (Var "In") Minus (Val 1))) 104 | (Assign "Out" (Op (Var "In") Times (Var "Out"))) 105 | 106 | 107 | {- Вычисление целой части квадратного корня 108 | 109 | B := 0; 110 | while (A >= B * B) { 111 | B++ 112 | }; 113 | B := B - 1 114 | -} 115 | squareRoot :: Statement 116 | squareRoot = undefined 117 | 118 | {- Вычисление числа Фибоначчи 119 | 120 | F0 := 1; 121 | F1 := 1; 122 | if (In == 0) { 123 | Out := F0 124 | } else { 125 | if (In == 1) { 126 | Out := F1 127 | } else { 128 | for (C := 2; C <= In; C++) { 129 | T := F0 + F1; 130 | F0 := F1; 131 | F1 := T; 132 | Out := T 133 | } 134 | } 135 | } 136 | -} 137 | fibonacci :: Statement 138 | fibonacci = undefined 139 | -------------------------------------------------------------------------------- /Lab2/stack.yaml: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by 'stack init' 2 | # 3 | # Some commonly used options have been documented as comments in this file. 4 | # For advanced use and comprehensive documentation of the format, please see: 5 | # https://docs.haskellstack.org/en/stable/yaml_configuration/ 6 | 7 | # Resolver to choose a 'specific' stackage snapshot or a compiler version. 8 | # A snapshot resolver dictates the compiler version and the set of packages 9 | # to be used for project dependencies. For example: 10 | # 11 | # resolver: lts-3.5 12 | # resolver: nightly-2015-09-21 13 | # resolver: ghc-7.10.2 14 | # 15 | # The location of a snapshot can be provided as a file or url. Stack assumes 16 | # a snapshot provided as a file might change, whereas a url resource does not. 17 | # 18 | # resolver: ./custom-snapshot.yaml 19 | # resolver: https://example.com/snapshots/2018-01-01.yaml 20 | resolver: 21 | url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/20/11.yaml 22 | 23 | # User packages to be built. 24 | # Various formats can be used as shown in the example below. 25 | # 26 | # packages: 27 | # - some-directory 28 | # - https://example.com/foo/bar/baz-0.0.2.tar.gz 29 | # subdirs: 30 | # - auto-update 31 | # - wai 32 | packages: 33 | - . 34 | # Dependency packages to be pulled from upstream that are not in the resolver. 35 | # These entries can reference officially published versions as well as 36 | # forks / in-progress versions pinned to a git hash. For example: 37 | # 38 | # extra-deps: 39 | # - acme-missiles-0.3 40 | # - git: https://github.com/commercialhaskell/stack.git 41 | # commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a 42 | # 43 | # extra-deps: [] 44 | 45 | # Override default flag values for local packages and extra-deps 46 | # flags: {} 47 | 48 | # Extra package databases containing global packages 49 | # extra-package-dbs: [] 50 | 51 | # Control whether we use the GHC we find on the path 52 | system-ghc: true 53 | # 54 | # Require a specific version of stack, using version ranges 55 | # require-stack-version: -any # Default 56 | # require-stack-version: ">=2.9" 57 | # 58 | # Override the architecture used by stack, especially useful on Windows 59 | # arch: i386 60 | # arch: x86_64 61 | # 62 | # Extra directories used by stack for building 63 | # extra-include-dirs: [/path/to/dir] 64 | # extra-lib-dirs: [/path/to/dir] 65 | # 66 | # Allow a newer minor version of GHC than the snapshot specifies 67 | # compiler-check: newer-minor 68 | -------------------------------------------------------------------------------- /Lab2/stack.yaml.lock: -------------------------------------------------------------------------------- 1 | # This file was autogenerated by Stack. 2 | # You should not edit this file by hand. 3 | # For more information, please see the documentation at: 4 | # https://docs.haskellstack.org/en/stable/lock_files 5 | 6 | packages: [] 7 | snapshots: 8 | - completed: 9 | sha256: adbc602422dde10cc330175da7de8609e70afc41449a7e2d6e8b1827aa0e5008 10 | size: 649342 11 | url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/20/11.yaml 12 | original: 13 | url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/20/11.yaml 14 | -------------------------------------------------------------------------------- /Lab2/test/Spec.hs: -------------------------------------------------------------------------------- 1 | import Poly 2 | import SimpleLang 3 | import Test.Hspec 4 | 5 | main :: IO () 6 | main = hspec $ do 7 | describe "poly" $ do 8 | it "applyPoly" $ pending 9 | describe "simpleLang" $ do 10 | -- включите тесты на работу 11 | it "desugar" $ pending 12 | -------------------------------------------------------------------------------- /Lab3/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog for `Lab3` 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to the 7 | [Haskell Package Versioning Policy](https://pvp.haskell.org/). 8 | 9 | ## Unreleased 10 | 11 | ## 0.1.0.0 - YYYY-MM-DD 12 | -------------------------------------------------------------------------------- /Lab3/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Author name here (c) 2023 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Author name here nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /Lab3/Lab3.cabal: -------------------------------------------------------------------------------- 1 | cabal-version: 1.12 2 | 3 | -- This file has been generated from package.yaml by hpack version 0.35.0. 4 | -- 5 | -- see: https://github.com/sol/hpack 6 | 7 | name: Lab3 8 | version: 0.1.0.0 9 | description: Please see the README on GitHub at 10 | homepage: https://github.com/githubuser/Lab3#readme 11 | bug-reports: https://github.com/githubuser/Lab3/issues 12 | author: Author name here 13 | maintainer: example@example.com 14 | copyright: 2023 Author name here 15 | license: BSD3 16 | license-file: LICENSE 17 | build-type: Simple 18 | extra-source-files: 19 | README.md 20 | CHANGELOG.md 21 | 22 | source-repository head 23 | type: git 24 | location: https://github.com/githubuser/Lab3 25 | 26 | library 27 | exposed-modules: 28 | FunctorsMonads 29 | Streams 30 | other-modules: 31 | Paths_Lab3 32 | hs-source-dirs: 33 | src 34 | ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -Wno-unused-matches 35 | build-depends: 36 | base >=4.7 && <5 37 | default-language: Haskell2010 38 | 39 | test-suite Lab3-test 40 | type: exitcode-stdio-1.0 41 | main-is: Spec.hs 42 | other-modules: 43 | Paths_Lab3 44 | hs-source-dirs: 45 | test 46 | ghc-options: -Wall -Wcompat -Widentities -Wincomplete-record-updates -Wincomplete-uni-patterns -Wmissing-home-modules -Wpartial-fields -Wredundant-constraints -Wno-unused-matches -threaded -rtsopts -with-rtsopts=-N 47 | build-depends: 48 | Lab3 49 | , base >=4.7 && <5 50 | , hedgehog-classes 51 | , hspec 52 | , hspec-hedgehog 53 | , quickcheck-classes 54 | default-language: Haskell2010 55 | -------------------------------------------------------------------------------- /Lab3/README.md: -------------------------------------------------------------------------------- 1 | Вам нужно реализовать функции, определённые в файлах в директории . Порядок: 2 | - Streams.hs 3 | - FunctorsMonads.hs 4 | В есть ещё дополнительное задание. 5 | Остальное смотрите в . 6 | -------------------------------------------------------------------------------- /Lab3/Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /Lab3/package.yaml: -------------------------------------------------------------------------------- 1 | name: Lab3 2 | version: 0.1.0.0 3 | github: "githubuser/Lab3" 4 | license: BSD3 5 | author: "Author name here" 6 | maintainer: "example@example.com" 7 | copyright: "2023 Author name here" 8 | 9 | extra-source-files: 10 | - README.md 11 | - CHANGELOG.md 12 | 13 | # Metadata used when publishing your package 14 | # synopsis: Short description of your package 15 | # category: Web 16 | 17 | # To avoid duplicated efforts in documentation and dealing with the 18 | # complications of embedding Haddock markup inside cabal files, it is 19 | # common to point users to the README.md file. 20 | description: Please see the README on GitHub at 21 | 22 | dependencies: 23 | - base >= 4.7 && < 5 24 | 25 | ghc-options: 26 | - -Wall 27 | - -Wcompat 28 | - -Widentities 29 | - -Wincomplete-record-updates 30 | - -Wincomplete-uni-patterns 31 | # - -Wmissing-export-lists 32 | - -Wmissing-home-modules 33 | - -Wpartial-fields 34 | - -Wredundant-constraints 35 | - -Wno-unused-matches 36 | 37 | library: 38 | source-dirs: src 39 | 40 | tests: 41 | Lab3-test: 42 | main: Spec.hs 43 | source-dirs: test 44 | ghc-options: 45 | - -threaded 46 | - -rtsopts 47 | - -with-rtsopts=-N 48 | dependencies: 49 | - Lab3 50 | - hspec 51 | - hspec-hedgehog 52 | - quickcheck-classes 53 | - hedgehog-classes 54 | -------------------------------------------------------------------------------- /Lab3/src/FunctorsMonads.hs: -------------------------------------------------------------------------------- 1 | module FunctorsMonads where 2 | 3 | -- определяем аналоги стандартных классов Functor и Monad, 4 | -- чтобы нам не мешали их существующие экземпляры 5 | 6 | -- документация на стандартные версии: 7 | -- https://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Functor.html 8 | -- https://hackage.haskell.org/package/base-4.12.0.0/docs/Control-Applicative.html 9 | -- https://hackage.haskell.org/package/base-4.12.0.0/docs/Control-Monad.html 10 | -- заметьте, что посмотрев в источники, можно взять реализацию оттуда; 11 | -- постарайтесь этого не делать. 12 | 13 | class Functor' f where 14 | (<$$>) :: (a -> b) -> f a -> f b 15 | 16 | infixl 4 <$$> 17 | 18 | class Functor' f => Applicative' f where 19 | pure' :: a -> f a 20 | (<**>) :: f (a -> b) -> f a -> f b 21 | 22 | infixl 4 <**> 23 | 24 | -- Задание 1 ----------------------------------------- 25 | 26 | -- реализуйте join' через >>== и наоборот 27 | class Applicative' m => Monad' m where 28 | (>>==) :: m a -> (a -> m b) -> m b 29 | (>>==) = error "implement using join' (and Applicative')" 30 | join' :: m (m a) -> m a 31 | join' = error "implement using >>== (and Applicative')" 32 | 33 | -- пример 34 | instance Functor' Maybe where 35 | _ <$$> Nothing = Nothing 36 | f <$$> Just x = Just (f x) 37 | instance Applicative' Maybe where 38 | pure' = Just 39 | Just f <**> Just x = Just (f x) 40 | _ <**> _ = Nothing 41 | instance Monad' Maybe where 42 | -- достаточно было бы определить одну из функций >>== и join' 43 | Nothing >>== _ = Nothing 44 | Just x >>== f = f x 45 | join' Nothing = Nothing 46 | join' (Just x) = x 47 | 48 | instance Functor' [] where 49 | f <$$> xs = map f xs 50 | instance Applicative' [] where 51 | pure' x = [x] 52 | fs <**> xs = [f x | f <- fs, x <- xs] 53 | instance Monad' [] where 54 | -- достаточно было бы определить одну из функций >>== и join' 55 | xs >>== f = [y | x <- xs, y <- f x] 56 | join' xss = [x | xs <- xss, x <- xs] 57 | 58 | -- Задание 2 ----------------------------------------- 59 | 60 | -- Реализуйте функции, следуя типам. 61 | -- liftA2' и seqA были на лекции, но: 62 | -- 1. можно попробовать восстановить самостоятельно 63 | -- 2. они могут быть использованы для других заданий 64 | 65 | -- Добавьте тесты! Используйте не только те примеры, которые приведены здесь. 66 | -- Попробуйте написать тесты _до_ реализации, чтобы проверить, правильных ли 67 | -- результатов вы ожидаете. Можно также использовать тесты свойств. 68 | 69 | -- "Поднимает" функцию от двух аргументов в функцию, работающую с действиями 70 | -- liftA2' (+) (Just 1) (Just 2) == Just 3 71 | -- liftA2' (+) Nothing (Just 2) == Nothing 72 | liftA2' :: Applicative' f => (a -> b -> c) -> f a -> f b -> f c 73 | liftA2' = undefined 74 | 75 | -- Выполняет все действия в списке и собирает их результаты в один список 76 | -- seqA [Just 1, Just 2] == Just [1, 2] 77 | -- seqA [Just 1, Just 2, Nothing] == Nothing 78 | seqA :: Applicative' f => [f a] -> f [a] 79 | seqA = undefined 80 | 81 | -- Применяет функцию, возвращающую действия, ко всем элементам списка, выполняет эти действия 82 | -- и собирает результаты в список 83 | -- traverseA Just [1, 2] == Just [1, 2] 84 | -- traverseA (\a -> if a > 2 then Just a else Nothing) [1, 3] == Nothing 85 | traverseA :: Applicative' f => (a -> f b) -> [a] -> f [b] 86 | traverseA = undefined 87 | 88 | -- Фильтрует список, используя "предикат с эффектом". 89 | -- filterA (\a -> if a > 10 then Nothing else Just (a > 0)) [-1, -2, 1, 2] == Just [1, 2] 90 | -- filterA (\a -> if a < 0 then Nothing else Just (a > 1)) [-1, -2, 1, 2] == Nothing 91 | filterA :: Applicative' f => (a -> f Bool) -> [a] -> f [a] 92 | filterA = undefined 93 | 94 | -- Композиция монадических функций 95 | -- composeM Just Just == Just (т.е. для всех x: composeM Just Just x == Just x) 96 | -- composeM Just (const Nothing) == const Nothing 97 | composeM :: Monad' m => (b -> m c) -> (a -> m b) -> (a -> m c) 98 | composeM = undefined 99 | 100 | -- Задание 3 ----------------------------------------- 101 | 102 | -- Реализуйте экземпляры классов типов 103 | 104 | -- Добавьте тесты на поведение функций из задания 2 с этими экземплярами 105 | 106 | instance Functor' (Either t) where 107 | (<$$>) = undefined 108 | instance Applicative' (Either t) where 109 | pure' = undefined 110 | (<**>) = undefined 111 | instance Monad' (Either t) where 112 | 113 | instance Functor' ((->) t) where -- (->) a b -- то же самое, что a -> b 114 | (<$$>) = undefined 115 | instance Applicative' ((->) t) where 116 | pure' = undefined 117 | (<**>) = undefined 118 | instance Monad' ((->) t) where 119 | -------------------------------------------------------------------------------- /Lab3/src/Streams.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -Wall #-} 2 | module Streams where 3 | 4 | import Data.List(intercalate) 5 | 6 | -- Задание 1 ----------------------------------------- 7 | 8 | -- Тип Stream a представляет бесконечные списки (потоки) значений типа a 9 | -- (в отличие от [a], которые могут быть как конечными, так и бесконечными 10 | data Stream a = a :> Stream a 11 | 12 | -- Экземпляр Show для Stream a печатает первые 10 элементов потока 13 | -- Для использования нужно определить sTake 14 | instance Show a => Show (Stream a) where 15 | show s = "[" ++ intercalate ", " (map show $ sTake 10 s) 16 | ++ ", ..." 17 | 18 | -- Реализуйте функцию, превращающую поток в (бесконечный) список 19 | streamToList :: Stream a -> [a] 20 | streamToList = undefined 21 | 22 | -- функция, возвращающая n первых элементов потока 23 | -- удобна для написания тестов следующих функций 24 | sTake :: Int -> Stream a -> [a] 25 | sTake = undefined 26 | 27 | -- Задание 2 ----------------------------------------- 28 | 29 | -- Реализуйте несколько простых функций для работы с потоками 30 | -- Не забудьте добавить тесты! 31 | 32 | -- поток, состоящий из одинаковых элементов 33 | sRepeat :: a -> Stream a 34 | sRepeat = undefined 35 | 36 | -- sRepeat 1 == [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ... 37 | 38 | -- поток, состоящий из бесконечного числа повторов списка 39 | -- (подсказка: эту и предыдущую можно реализовать так, что полученный поток 40 | -- будет циклическим (ссылаться сам на себя), а не бесконечно растущим) 41 | -- sCycle [1, 2, 3] == [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, ... 42 | sCycle :: [a] -> Stream a 43 | sCycle = undefined 44 | 45 | -- поток, заданный начальным значением и функцией, строящей следующее значение 46 | -- по текущему 47 | -- sIterate (/ 2) 1.0 == [1.0, 0.5, 0.25, 0.125, 0.0625, ... 48 | sIterate :: (a -> a) -> a -> Stream a 49 | sIterate = undefined 50 | 51 | -- функция, возвращающая поток из чередующихся элементов двух потоков 52 | -- (для следующего задания нужно сделать эту функцию ленивой по 53 | -- второму аргументу, то есть не сопоставлять его с образцом) 54 | sInterleave :: Stream a -> Stream a -> Stream a 55 | sInterleave (_ :> _) _ = undefined 56 | 57 | -- sInterleave (sRepeat 1) (sRepeat 2) == [1, 2, 1, 2, 1, 2, ... 58 | 59 | -- Задание 3 ----------------------------------------- 60 | 61 | -- Используя предыдущие функции, реализуйте 62 | 63 | -- поток натуральных чисел (начиная с 0) 64 | nats :: Stream Integer 65 | nats = undefined 66 | 67 | -- nats == [0, 1, 2, 3, 4, 5, 6, 7, ... 68 | 69 | -- поток, n-ный элемент которого (начиная с 1) -- максимальная степень 2, 70 | -- делящая n нацело. Подсказка: с помощью sInterleave это можно сделать без 71 | -- проверок на делимость, если её реализация ленива по второму аргументу 72 | -- (подумайте, почему это важно). 73 | ruler :: Stream Integer 74 | ruler = undefined 75 | 76 | -- ruler == [0, 1, 0, 2, 0, 1, 0, 3, ... 77 | 78 | -- Задание 4 ----------------------------------------- 79 | 80 | minMaxSlow, minMax, minMaxBang :: Ord a => [a] -> Maybe (a, a) 81 | {- -O0: Total time: ??? Total Memory in use: ??? -} 82 | {- -O2: Total time: ??? Total Memory in use: ??? -} 83 | minMaxSlow [] = Nothing 84 | minMaxSlow xs = Just (minimum xs, maximum xs) 85 | 86 | -- функция minMax должна находить минимальное и максимальное значение списка, 87 | -- так же, как minMaxSlow. Проблема с minMaxSlow в том, что она проходит по списку два раза 88 | -- и поэтому вынуждена сохранять его в памяти целиком. Реализуйте minMax так, чтобы 89 | -- сделать только один проход по списку. 90 | 91 | {- -O0: Total time: ??? Total Memory in use: ??? -} 92 | {- -O2: Total time: ??? Total Memory in use: ??? -} 93 | minMax = undefined 94 | 95 | -- Дополнительное задание: реализуйте ту же самую функцию (под названием minMaxBang) с 96 | -- использованием явной строгости (seq и/или !) 97 | 98 | {- -O0: Total time: ??? Total Memory in use: ??? -} 99 | {- -O2: Total time: ??? Total Memory in use: ??? -} 100 | minMaxBang = undefined 101 | 102 | -- Скомпилируйте программу с аргументами `ghc Streams.hs -O2 -rtsopts -main-is Streams` 103 | -- и запустите `Streams.exe +RTS -s` (`./Streams +RTS -s` в Linux/OSX). 104 | -- Документацию можете найти в https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/runtime_control.html#rts-options-to-produce-runtime-statistics 105 | -- Сравните время выполнения и общую память для разных вариантов. 106 | -- Также посмотрите на эффект при отключении оптимизаций (-O0) 107 | 108 | main :: IO () 109 | main = print $ minMaxSlow $ sTake 1000000 $ ruler 110 | -- main = print $ minMax $ sTake 1000000 $ ruler 111 | -- main = print $ minMaxBang $ sTake 1000000 $ ruler 112 | 113 | -- Задание 5 ----------------------------------------- 114 | 115 | -- Добавьте тесты свойств (на QuickCheck или Hedgehog) для функций из заданий 1--4. 116 | -- http://hackage.haskell.org/package/QuickCheck 117 | -- https://github.com/hedgehogqa/haskell-hedgehog 118 | 119 | -- Задание 6 ----------------------------------------- 120 | 121 | -- Делать после или вместе с FunctorsMonads (задание помещено здесь, чтобы избежать экземпляров-сирот) 122 | 123 | -- Реализуйте экземпляры классов для потоков (модуль Streams) 124 | -- Проверьте законы! Здесь легко написать реализации, которая подходит по типам, но законы для неё не выполняются. 125 | -- Подсказка: тип Stream a изоморфен Nat -> a (Nat -- тип натуральных чисел). 126 | -- Для тестирования законов можно использовать библиотеку quickcheck-classes (не входит в Haskell Platform): 127 | -- http://hackage.haskell.org/package/quickcheck-classes 128 | -- или http://hackage.haskell.org/package/hedgehog-classes, если в предыдущем задании использовали Hedgehog. 129 | 130 | instance Functor Stream where 131 | fmap = undefined 132 | 133 | instance Applicative Stream where 134 | pure = undefined 135 | (<*>) = undefined 136 | 137 | instance Monad Stream where 138 | return = pure 139 | -- в этом случае может быть проще использовать реализацию через join 140 | -- xs >>= f = join ... where join = ... 141 | (>>=) = undefined 142 | 143 | -- https://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Foldable.html 144 | instance Foldable Stream where 145 | -- достаточно определить одну из них 146 | -- foldr = undefined 147 | -- foldMap = undefined 148 | 149 | -- https://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Traversable.html 150 | instance Traversable Stream where 151 | -- достаточно определить одну из них 152 | -- traverse = undefined 153 | -- sequenceA = undefined 154 | -------------------------------------------------------------------------------- /Lab3/stack.yaml: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by 'stack init' 2 | # 3 | # Some commonly used options have been documented as comments in this file. 4 | # For advanced use and comprehensive documentation of the format, please see: 5 | # https://docs.haskellstack.org/en/stable/yaml_configuration/ 6 | 7 | # Resolver to choose a 'specific' stackage snapshot or a compiler version. 8 | # A snapshot resolver dictates the compiler version and the set of packages 9 | # to be used for project dependencies. For example: 10 | # 11 | # resolver: lts-3.5 12 | # resolver: nightly-2015-09-21 13 | # resolver: ghc-7.10.2 14 | # 15 | # The location of a snapshot can be provided as a file or url. Stack assumes 16 | # a snapshot provided as a file might change, whereas a url resource does not. 17 | # 18 | # resolver: ./custom-snapshot.yaml 19 | # resolver: https://example.com/snapshots/2018-01-01.yaml 20 | resolver: 21 | url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/20/11.yaml 22 | 23 | # User packages to be built. 24 | # Various formats can be used as shown in the example below. 25 | # 26 | # packages: 27 | # - some-directory 28 | # - https://example.com/foo/bar/baz-0.0.2.tar.gz 29 | # subdirs: 30 | # - auto-update 31 | # - wai 32 | packages: 33 | - . 34 | # Dependency packages to be pulled from upstream that are not in the resolver. 35 | # These entries can reference officially published versions as well as 36 | # forks / in-progress versions pinned to a git hash. For example: 37 | # 38 | # extra-deps: 39 | # - acme-missiles-0.3 40 | # - git: https://github.com/commercialhaskell/stack.git 41 | # commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a 42 | # 43 | # extra-deps: [] 44 | 45 | # Override default flag values for local packages and extra-deps 46 | # flags: {} 47 | 48 | # Extra package databases containing global packages 49 | # extra-package-dbs: [] 50 | 51 | # Control whether we use the GHC we find on the path 52 | system-ghc: true 53 | # 54 | # Require a specific version of stack, using version ranges 55 | # require-stack-version: -any # Default 56 | # require-stack-version: ">=2.9" 57 | # 58 | # Override the architecture used by stack, especially useful on Windows 59 | # arch: i386 60 | # arch: x86_64 61 | # 62 | # Extra directories used by stack for building 63 | # extra-include-dirs: [/path/to/dir] 64 | # extra-lib-dirs: [/path/to/dir] 65 | # 66 | # Allow a newer minor version of GHC than the snapshot specifies 67 | # compiler-check: newer-minor 68 | -------------------------------------------------------------------------------- /Lab3/stack.yaml.lock: -------------------------------------------------------------------------------- 1 | # This file was autogenerated by Stack. 2 | # You should not edit this file by hand. 3 | # For more information, please see the documentation at: 4 | # https://docs.haskellstack.org/en/stable/lock_files 5 | 6 | packages: [] 7 | snapshots: 8 | - completed: 9 | sha256: adbc602422dde10cc330175da7de8609e70afc41449a7e2d6e8b1827aa0e5008 10 | size: 649342 11 | url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/20/11.yaml 12 | original: 13 | url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/20/11.yaml 14 | -------------------------------------------------------------------------------- /Lab3/test/Spec.hs: -------------------------------------------------------------------------------- 1 | import FunctorsMonads 2 | import Streams hiding (main) 3 | import Test.Hspec 4 | -- Раскомментируйте QuickCheck или Hegdehog, в зависимости от того, что будете использовать 5 | -- Документация https://hspec.github.io/quickcheck.html 6 | -- import Test.Hspec.QuickCheck 7 | -- Документация в https://github.com/parsonsmatt/hspec-hedgehog#readme 8 | -- import Test.Hspec.Hedgehog 9 | 10 | -- Добавьте минимум 5 тестов свойств для функций из первых 2 лабораторных (скопируйте определения тестируемых функций сюда). 11 | 12 | main :: IO () 13 | main = hspec $ do 14 | describe "functors and monads" $ do 15 | it "" $ pending 16 | describe "streams" $ do 17 | it "" $ pending 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Презентации и задания к курсу по Haskell в МИЭТ. 2 | -------------------------------------------------------------------------------- /Typeclassopedia-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexeyr/miet-haskell-course/2d8def951630ff403c8843e7c115e8bb100c3080/Typeclassopedia-diagram.png -------------------------------------------------------------------------------- /concurrency_eval1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexeyr/miet-haskell-course/2d8def951630ff403c8843e7c115e8bb100c3080/concurrency_eval1.png -------------------------------------------------------------------------------- /concurrency_eval2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexeyr/miet-haskell-course/2d8def951630ff403c8843e7c115e8bb100c3080/concurrency_eval2.png -------------------------------------------------------------------------------- /latexindent.yaml: -------------------------------------------------------------------------------- 1 | defaultIndent: " " 2 | 3 | verbatimEnvironments: 4 | haskell: 1 5 | haskell*: 1 6 | 7 | verbatimCommands: 8 | haskinline: 1 9 | -------------------------------------------------------------------------------- /lecture1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexeyr/miet-haskell-course/2d8def951630ff403c8843e7c115e8bb100c3080/lecture1.pdf -------------------------------------------------------------------------------- /lecture1.tex: -------------------------------------------------------------------------------- 1 | \documentclass[10pt]{beamer} 2 | \input{lecture_preamble.tex} 3 | 4 | \title{Лекция 1: введение и основы синтаксиса} 5 | 6 | \begin{document} 7 | \begin{frame}[plain] 8 | \maketitle 9 | \end{frame} 10 | 11 | \begin{frame} 12 | \frametitle{Организация курса} 13 | \begin{itemize} 14 | \item 8 лекций 15 | \item 4 лабораторных 16 | \item Итоговый проект 17 | \end{itemize} 18 | \end{frame} 19 | 20 | \begin{frame} 21 | \frametitle{Парадигмы программирования} 22 | \begin{itemize} 23 | \item Что такое парадигма? 24 | \pause 25 | \begin{quote} 26 | \enquote{Совокупность идей и понятий, определяющих стиль написания компьютерных программ.} (Wikipedia) 27 | \end{quote} 28 | \pause 29 | \item Основные парадигмы: 30 | \pause 31 | \begin{itemize} 32 | \item Структурное программирование 33 | \item Процедурное программирование 34 | \item \textbf{Функциональное программирование} 35 | \item Логическое программирование 36 | \item Объектно-ориентированное программирование 37 | \end{itemize} 38 | \pause 39 | \item В парадигме важно не только то, что используется, но то, использование чего не допускается или минимизируется. 40 | \pause 41 | \item Например, \haskinline!goto! в структурном программировании, глобальные переменные в ООП. 42 | \end{itemize} 43 | \end{frame} 44 | 45 | \begin{frame} 46 | \includegraphics[trim={0 0 144mm -2mm},clip,width=\textwidth,height=\textheight,keepaspectratio]{paradigmsDIAGRAMeng108.pdf} 47 | \footnotesize \href{https://www.info.ucl.ac.be/~pvr/paradigms.html}{Peter Van Roy, "Programming Paradigms for Dummies: What Every Programmer Should Know", 2009} 48 | \end{frame} 49 | 50 | \begin{frame} 51 | \frametitle{Функциональное программирование} 52 | \begin{itemize} 53 | \item Значения лучше переменных. 54 | \begin{itemize} 55 | \item Переменная даёт имя значению или функции, а не адресу в памяти. 56 | \item Переменные неизменяемы. 57 | \item Типы данных неизменяемы. 58 | \end{itemize} 59 | \item Выражения лучше инструкций. 60 | \begin{itemize} 61 | \item Аналоги \haskinline|if|, \haskinline|try-catch| и т.д. "--- выражения. 62 | \end{itemize} 63 | \item Функции как в математике (следующий слайд) 64 | \end{itemize} 65 | \end{frame} 66 | 67 | 68 | \begin{frame} 69 | \frametitle{Функциональное программирование} 70 | \begin{itemize} 71 | \item Функции как в математике\pause 72 | \begin{itemize} 73 | \item Чистые функции: аргументу соответствует результат, а всё прочее от лукавого. 74 | \begin{itemize} 75 | \item Нет побочных эффектов (ввода-вывода, обращения к внешней памяти, не связанной с аргументом, и т.д.) 76 | \item При одинаковых аргументах результаты такой функции одинаковы 77 | \end{itemize} 78 | \item Функции являются значениями (функции первого класса) 79 | \item Функции часто принимают и возвращают функции (функции высших порядков) 80 | \end{itemize} 81 | \pause 82 | \item Опора на математические теории: лямбда-исчисление, теория типов, теория категорий 83 | \end{itemize} 84 | \end{frame} 85 | 86 | \begin{frame} 87 | \frametitle{Языки ФП} 88 | \begin{itemize} 89 | \item семейство Lisp: первый ФП-язык и один из первых языков высокого уровня вообще 90 | \item Erlang и Elixir: упор на многозадачность (модель акторов), надёжность 91 | \item Scala, Kotlin, F\#: гибриды с ООП для JVM и для CLR 92 | \item Purescript, Elm, Ur/Web: для веба 93 | \item Семейство ML: OCaml, SML, F\# 94 | \pause 95 | \item \textbf{Haskell:} 96 | \begin{itemize} 97 | \item Чисто функциональный 98 | \pause 99 | \item Строго статически типизированный (с очень мощной и выразительной системой типов) 100 | \pause 101 | \item Ленивый 102 | \end{itemize} 103 | \end{itemize} 104 | \end{frame} 105 | 106 | \begin{frame}[fragile] 107 | \frametitle{Язык Haskell: начало} 108 | \begin{itemize} 109 | \item Установите ghcup (\url{https://www.haskell.org/ghcup/}) 110 | \item Запустите GHCi 111 | \item Это оболочка или REPL (Read-Eval-Print loop) 112 | \begin{itemize} 113 | \item Read: Вы вводите выражения Haskell (и команды GHCi) 114 | \item Eval: GHCi вычисляет результат 115 | \item Print: и выводит его на экран 116 | \end{itemize} 117 | \item Пример: 118 | \begin{ghci} 119 | GHCi, version 9.2.5: http://www.haskell.org/ghc/ :? for help 120 | \end{ghci} 121 | \begin{haskell} 122 | ghci> 2 + 2 123 | 4 124 | ghci> :t True -- команда GHCi 125 | True :: Bool 126 | \end{haskell} 127 | \end{itemize} 128 | \end{frame} 129 | 130 | \begin{frame}[fragile] 131 | \frametitle{Язык Haskell: начало} 132 | \begin{itemize} 133 | \item \haskinline|2 + 2|, \haskinline|True| "--- выражения 134 | \item \haskinline|4|, \haskinline|True| "--- значения 135 | \item \haskinline|Bool| "--- тип 136 | \pause 137 | \item Значение "--- \enquote{вычисленное до конца} выражение. 138 | \item Тип (статический) "--- множество значений и выражений, построенное по таким правилам, что компилятор может определить типы и проверить отсутствие ошибок в них без запуска программы. 139 | \item От типа зависит то, какие операции допустимы: 140 | \begin{haskell} 141 | ghci> True + False 142 | \end{haskell} 143 | \begin{ghci} 144 | :12:1: error: 145 | No instance for (Num Bool) arising from a use of '+' 146 | In the expression: True + False 147 | In an equation for 'it': it = True + False 148 | \end{ghci} 149 | \item Это ошибка компиляции, а не выполнения. 150 | \end{itemize} 151 | \end{frame} 152 | 153 | \begin{frame}[fragile] 154 | \frametitle{Вызов функций} 155 | \begin{itemize} 156 | \item Вызов (применение) функции пишется без скобок: \haskinline|f x|, \haskinline|foo x y|. 157 | \item Скобки используются, когда аргументы "--- сложные выражения: \haskinline|f (g x)| 158 | \item И внутри сложных выражений вообще. 159 | \item Бинарные операторы (как \haskinline|+|) это просто функции с именем из символов вместо букв и цифр. 160 | \begin{itemize} 161 | \item Можно писать их префиксно, заключив в скобки: \haskinline[breaklines=false]|(+) 2 2|. 162 | \item А любую функцию двух аргументов с алфавитным именем можно писать инфиксно между обратными апострофами: 163 | \haskinline|4 `div` 2|. 164 | \item Единственный небинарный оператор "--- унарный \haskinline|-|. 165 | \end{itemize} 166 | \item Названия переменных и функций начинаются со строчной буквы (кроме операторов). 167 | \end{itemize} 168 | \end{frame} 169 | 170 | \begin{frame}[fragile] 171 | \frametitle{Определение функций и переменных} 172 | \begin{itemize} 173 | \item Определение переменной выглядит как в математике, даже без ключевых слов: 174 | \begin{haskell} 175 | название = значение 176 | \end{haskell} 177 | \item Определение функции почти такое же: 178 | \begin{haskell} 179 | название параметр1 параметр2 = значение 180 | \end{haskell} 181 | \item Тело функции это не блок, а одно выражение (но сколь угодно сложное). 182 | \begin{haskell} 183 | ghci> x = sin pi 184 | ghci> x !\pause! 185 | 1.2246063538223773e-16 186 | ghci> square x = x * x 187 | ghci> square 2 188 | 4 189 | \end{haskell} 190 | \end{itemize} 191 | \end{frame} 192 | 193 | \begin{frame}[fragile] 194 | \frametitle{Базовые типы} 195 | \begin{itemize} 196 | \item Названия типов всегда с заглавной буквы. 197 | \item \haskinline|Bool|: логические значения \haskinline|True| и \haskinline|False|. 198 | \item Целые числа: 199 | \begin{itemize} 200 | \item \haskinline|Integer|: неограниченные (кроме размера памяти); 201 | \item \haskinline|Int|: машинные\footnote{по cтандарту минимум 30 бит, но в GHC именно 32 или 64 бита}, \haskinline|Word|: машинные без знака; 202 | \item \haskinline|Data.{Int.Int/Word.Word}{8/16/32/64}|: фиксированного размера в битах, со знаком и без. 203 | \end{itemize} 204 | \item \haskinline|Float| и \haskinline|Double|: 32- и 64-битные числа с плавающей точкой, по стандарту IEEE-754. 205 | \item \haskinline|Character|: символы Unicode. 206 | \item \haskinline|()|: \enquote{Единичный тип} (unit) с единственным значением \haskinline|()|. 207 | \end{itemize} 208 | \end{frame} 209 | 210 | \begin{frame}[fragile] 211 | \frametitle{Тип функций и сигнатуры} 212 | \begin{itemize} 213 | \item Типы функций записываются через \haskinline|->|. Например, \haskinline|Int -> Char| это тип функции из \haskinline|Int| в \haskinline|Char|. 214 | 215 | \item Для нескольких аргументов это выглядит как \pause\haskinline|Bool -> Bool -> Bool|. 216 | 217 | \item \haskinline|::| читается как \enquote{имеет тип}. 218 | 219 | \item Запись \haskinline|выражение :: тип| "--- \enquote{сигнатура типа}. 220 | 221 | \item При объявлении экспортируемой функции или переменной сигнатура обычно указывается явно: 222 | \begin{haskell} 223 | foo :: Int -> Char 224 | foo x = ... 225 | \end{haskell} 226 | \item Компилятор обычно может вывести типы сам, но это защищает от \emph{непреднамеренного} изменения. 227 | \end{itemize} 228 | \end{frame} 229 | 230 | \begin{frame}[fragile] 231 | \frametitle{Арифметика} 232 | \begin{itemize} 233 | \item Это упрощённая версия. 234 | \item Полное объяснение требует понятия, которое будет введено позже. 235 | \item Например, команды \haskinline|:type 1| или \haskinline|:type (+)| дадут тип, который понимать пока не требуется. 236 | \item То же относится к ошибкам вроде \ghcinline[breaklines=false]|No instance for (Num Bool)...|\\ на более раннем слайде. 237 | \item Пока достаточно понимать, что есть несколько числовых типов. 238 | \item Они делятся на целочисленные (\haskinline|Int|, \haskinline|Integer|) и дробные (\haskinline|Float|, \haskinline|Double|). 239 | \end{itemize} 240 | \end{frame} 241 | 242 | \begin{frame}[fragile] 243 | \frametitle{Числовые литералы} 244 | \begin{itemize} 245 | \item Числовые литералы выглядят, как в других языках: \haskinline|0|, \haskinline|1.5|, \haskinline|1.2E-1|, \haskinline|0xDEADBEEF|. 246 | \item Целочисленные литералы могут иметь любой числовой тип, а дробные любой дробный. 247 | \item Но это относится \emph{только} к литералам. 248 | \item Неявного приведения (например, \haskinline|Int| в \haskinline|Double|) в Haskell \emph{нет}. 249 | \end{itemize} 250 | \end{frame} 251 | 252 | \begin{frame}[fragile] 253 | \frametitle{Приведение числовых типов} 254 | \begin{itemize} 255 | \item Используйте \haskinline|fromIntegral| для приведения из любого целочисленного типа в любой числовой. Тип-цель можно указать явно: 256 | \begin{haskell} 257 | ghci> let {x :: Integer; x = 2} 258 | ghci> x :: Double 259 | \end{haskell} 260 | \begin{ghci} 261 | :22:1: error: 262 | Couldn't match expected type 'Double' with actual type 'Integer' ... 263 | \end{ghci} 264 | \begin{haskell} 265 | ghci> fromIntegral x :: Double 266 | 2.0 267 | \end{haskell} 268 | \item А может выводиться из контекста: 269 | \begin{haskell} 270 | ghci> :t fromIntegral 2 / (4 :: Double) 271 | fromIntegral 2 / (4 :: Double) :: Double 272 | \end{haskell} 273 | \end{itemize} 274 | \end{frame} 275 | 276 | \begin{frame}[fragile] 277 | \frametitle{Приведение числовых типов} 278 | \begin{itemize} 279 | \item \haskinline|toInteger| переводит любой целочисленный тип \\в \haskinline|Integer|. 280 | \item \haskinline|fromInteger| "--- наоборот. Если аргумент слишком велик, возвращает значение по модулю: 281 | \begin{haskell} 282 | ghci> fromInteger (2^64) :: Int 283 | 0 284 | \end{haskell} 285 | \item \haskinline|toRational| и \haskinline|fromRational| "--- аналогично для \haskinline|Rational| и дробных типов. 286 | \item \haskinline|ceiling|, \haskinline|floor|, \haskinline|truncate| и \haskinline|round| "--- из дробных типов в целочисленные. 287 | \end{itemize} 288 | \end{frame} 289 | 290 | \begin{frame}[fragile] 291 | \frametitle{Арифметические операции} 292 | \begin{itemize} 293 | \item Операции \haskinline|+|, \haskinline|-|, \haskinline|*| "--- как обычно (унарного \haskinline|+| нет). 294 | \item \haskinline|/| "--- деление \emph{дробных} чисел. 295 | \begin{itemize} 296 | \item Использование \haskinline|/| для целых чисел даст ошибку \ghcinline|No instance... arising from a use of '/'|. Нужно сначала использовать \haskinline|fromIntegral|. 297 | \end{itemize} 298 | \item \haskinline|div| "--- деление нацело 299 | \item \haskinline|mod| "--- остаток 300 | \item 301 | \haskinline|quot| и \haskinline|rem| тоже, но отличаются от них поведением на отрицательных числах. 302 | \item \haskinline|^| "--- возведение любого числа в неотрицательную целую степень. 303 | \item \haskinline|^^| "--- дробного в любую целую. 304 | \item \haskinline|**| "--- дробного в степень того же типа. 305 | \end{itemize} 306 | \end{frame} 307 | 308 | \begin{frame}[fragile] 309 | \frametitle{Операции сравнения и\\логические операции} 310 | \begin{itemize} 311 | \item Большинство операций сравнения выглядят как обычно: \haskinline|==|, \haskinline|>|, \haskinline|<|, \haskinline|>=|, \haskinline|<=|. 312 | \item Но $\neq$ обозначается как \haskinline|/=|. 313 | \item Функция \haskinline|compare| возвращает \haskinline|Ordering|: тип с тремя значениями \haskinline|LT|, \haskinline|EQ| и \haskinline|GT|. 314 | \item Есть функции \haskinline|min| и \haskinline|max|. 315 | \item[] 316 | \item \enquote{и} это \haskinline|&&|, а \enquote{или} "--- \haskinline!||!, как обычно. 317 | \item \enquote{не} "--- \haskinline|not|. 318 | \end{itemize} 319 | \end{frame} 320 | 321 | %\begin{frame} 322 | % \href{run:./lecture2.pdf}{\beamergotobutton{Следующая лекция}} 323 | %\end{frame} 324 | \end{document} 325 | -------------------------------------------------------------------------------- /lecture10_monads.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexeyr/miet-haskell-course/2d8def951630ff403c8843e7c115e8bb100c3080/lecture10_monads.pdf -------------------------------------------------------------------------------- /lecture10_monads.tex: -------------------------------------------------------------------------------- 1 | % !TeX document-id = {0c096819-60cf-4d9a-a18e-64c3d748af84} 2 | % !TeX TXS-program:compile = txs:///pdflatex/[-shell-escape] 3 | 4 | \documentclass[11pt]{beamer} 5 | \input{lecture_preamble.tex} 6 | 7 | \title{Лекция 10: монады} 8 | 9 | \begin{document} 10 | \begin{frame}[plain] 11 | \maketitle 12 | \end{frame} 13 | 14 | \begin{frame}[fragile] 15 | \frametitle{Монады} 16 | \begin{itemize} 17 | \item Монады расширяют возможности аппликативных функторов так же, как те расширяют возможности функторов. 18 | \item А именно, там добавляется 19 | \begin{haskell} 20 | class Applicative m => Monad m where 21 | (>>=) :: m a -> (a -> m b) -> m b 22 | (>>) :: m a -> m b -> m b 23 | mx >> my =!\pause! mx >>=!\pause! \_ -> my 24 | \end{haskell} 25 | \pause 26 | \item По историческим причинам есть ещё \haskinline|return = pure|. 27 | \pause 28 | \item Больше похоже на знакомые типы, если поменять аргументы местами: 29 | \begin{haskell} 30 | fmap :: (a -> b) -> f a -> f b 31 | (<*>) :: f (a -> b) -> f a -> f b 32 | (=<<) :: (a -> m b) -> m a -> m b 33 | \end{haskell} 34 | \end{itemize} 35 | \end{frame} 36 | 37 | \begin{frame}[fragile] 38 | \frametitle{Что можно сделать с монадами} 39 | \begin{itemize} 40 | \item В прошлой лекции: 41 | \begin{displayquote} 42 | Структура результата аппликативного вычисления зависит только от структуры аргументов, а не от значений внутри них 43 | \end{displayquote} 44 | \item Монады снимают это ограничение. Теперь можно написать 45 | \begin{haskell} 46 | ifM :: Monad m => m Bool -> m a -> m a -> m a 47 | ifM mCond mThen mElse = mCond >>=!\pause! \cond ->!\pause! 48 | if cond then mThen else mElse 49 | \end{haskell} 50 | и проверить: 51 | \begin{haskell} 52 | ghci> ifM [True] [1] [2,3] 53 | [1] 54 | ghci> ifM [False] [1] [2,3] 55 | [2,3] 56 | \end{haskell} 57 | \end{itemize} 58 | \end{frame} 59 | 60 | 61 | \begin{frame}[fragile] 62 | \frametitle{Стрелки Клейсли} 63 | \begin{itemize} 64 | \item Тип второго аргумента \haskinline|(>>=)|, \haskinline|a -> m b|, где \haskinline|m| "--- монада, называется \emph{стрелкой Клейсли}. 65 | \item \haskinline|a| и \haskinline|b| могут быть как переменными типа, так и конкретными типами. 66 | \item Стрелки Клейсли можно композировать: 67 | \begin{haskellsmall} 68 | (>=>) :: Monad m => (a -> m b) -> (b -> m c) -> 69 | (a -> m c) 70 | f >=> g = \x ->!\pause! f x >>=!\pause! g 71 | \end{haskellsmall} 72 | \item Есть и аналоги \haskinline|filter|, \haskinline|map| и т.д., работающие со стрелками Клейсли. Их можно найти в \href{https://hackage.haskell.org/package/base-4.18.0.0/docs/Control-Monad.html}{документации \haskinline{Control.Monad}}. 73 | \end{itemize} 74 | \end{frame} 75 | 76 | \begin{frame}[fragile] 77 | \frametitle{Законы монад} 78 | \begin{itemize} 79 | \item \haskinline|pure x >>= f| $\equiv$ \haskinline|f x| 80 | \item \haskinline|mx >>= pure| $\equiv$ \haskinline|mx| 81 | \item \haskinline|(mx >>= f) >>= g| $\equiv$ \haskinline|mx >>= (\x -> f x >>= g)| 82 | \item Может быть, они не совсем интуитивны. 83 | \pause 84 | \item С помощью монадической композиции они записываются естественнее: 85 | \item \haskinline|pure >=> f| $\equiv$ \haskinline|f| 86 | \item \haskinline|f >=> pure| $\equiv$ \haskinline|f| 87 | \item \haskinline|(f >=> g) >=> h| $\equiv$ \haskinline|f >=> (g >=> h)| \pause 88 | \item Кроме этого, должно быть согласование с \haskinline|<*>|: \\ 89 | \haskinline|mf <*> mx| $\equiv$ \haskinline|mf >>= (\f -> mx >>= (\x -> pure (f x)))| 90 | (тоже громоздко, но удобная запись немного позже) 91 | \end{itemize} 92 | \end{frame} 93 | 94 | \begin{frame}[fragile] 95 | \frametitle{Примеры монад} 96 | \begin{itemize}[<+->] 97 | \item \haskinline|Maybe| "--- монада. Определим: 98 | \begin{haskell} 99 | instance Monad Maybe where 100 | -- (>{}>=) ::\pause Maybe a -> (a -> Maybe b) -> Maybe b 101 | Nothing >>= _ =!\pause! Nothing 102 | Just x >>= f =!\pause! f x 103 | \end{haskell} 104 | \item Списки тоже монада: 105 | \begin{haskell} 106 | -- (>{}>=) ::\pause [a] -> (a -> [b]) -> [b] 107 | [] >>= _ =!\pause! [] 108 | (x:xs) >>= f =!\pause! f x ++ (xs >>= f) 109 | \end{haskell} 110 | Или проще: 111 | \begin{haskell} 112 | xs >>= f = [y |!\pause! x <- xs, y <- f x] 113 | \end{haskell} 114 | \end{itemize} 115 | \end{frame} 116 | 117 | \begin{frame}[fragile] 118 | \frametitle{Ещё примеры монад} 119 | \begin{itemize} 120 | \item \haskinline|instance Monad Identity|, где \haskinline|newtype Identity a = Identity a|. 121 | \item \haskinline|instance Monad (Either c)|. 122 | \item \haskinline|instance Monad ((->) c)|: функции с фиксированным типом аргумента. 123 | \item \haskinline|instance Monoid c => Monad ((,) c)|: пары с фиксированным типом первого элемента, если этот тип "--- моноид. 124 | \end{itemize} 125 | \end{frame} 126 | 127 | \begin{frame}[fragile] 128 | \frametitle{\ldots и не монад} 129 | \begin{itemize} 130 | \item Есть и примеры аппликативных функторов, для которых нельзя определить экземпляр монады: 131 | \item 132 | \begin{haskell} 133 | newtype ConstInt a = ConstInt Int 134 | fmap f (ConstInt x) = ConstInt x 135 | pure _ = 0 136 | ConstInt x <*> ConstInt y = ConstInt (x + y) 137 | \end{haskell} 138 | \item Легко увидеть, что \haskinline|pure x >>= f| $\equiv$ \haskinline|f x| не может выполняться ни для какого определения \haskinline|>>=|: правая часть зависит от \haskinline|x|, а левая нет. 139 | \pause 140 | \item Ещё пример "--- \haskinline|ZipList| (если не допускать \emph{только} бесконечные списки, как в домашнем задании, или активное использование $\bot$). 141 | \end{itemize} 142 | \end{frame} 143 | 144 | \begin{frame}[fragile] 145 | \frametitle{\haskinline|do|-нотация} 146 | \begin{itemize} 147 | \item Для монад есть специальный синтаксис, которым часто удобнее пользоваться. 148 | \item Скажем, у нас есть цепочка операций 149 | \begin{haskellsmall} 150 | action1 >>= (\x1 -> action2 x1 >>= 151 | (\x2 -> action3 x1 x2 >> action4 x1)) 152 | \end{haskellsmall} 153 | \item 154 | Сначала перепишем так: 155 | \begin{haskellsmall} 156 | action1 >>= \x1 -> 157 | action2 x1 >>= \x2 -> 158 | action3 x1 x2 >> 159 | action4 x1 160 | \end{haskellsmall} 161 | \item 162 | В \haskinline|do|-блоке строки вида \haskinline|action1 >>= \x1 ->| превращаются в \haskinline|x1 <- action1|, а \haskinline|>>| пропадает: 163 | \begin{haskellsmall} 164 | do x1 <- action1 165 | x2 <- action2 x1 166 | action3 x1 x2 167 | action4 x1 168 | \end{haskellsmall} 169 | \end{itemize} 170 | \end{frame} 171 | 172 | \begin{frame}[fragile] 173 | \frametitle{Законы монад в \haskinline|do|-нотации} 174 | \begin{itemize}[<+->] 175 | \item Законы монад также можно записать через \haskinline|do|: 176 | \item 177 | \begin{haskell} 178 | do y <- pure x !$\mathtt{\equiv}$! f x 179 | f y 180 | \end{haskell} 181 | \item 182 | \begin{haskell} 183 | do x <- mx !$\mathtt{\equiv}$! mx 184 | pure x 185 | \end{haskell} 186 | \item 187 | \begin{haskell} 188 | do x <- mx !$\mathtt{\equiv}$! do y <- do x <- mx 189 | y <- f x !\textcolor{white}{$\mathtt{\equiv}$}! f x 190 | g y !\textcolor{white}{$\mathtt{\equiv}$}! g y 191 | \end{haskell} 192 | \item 193 | \begin{haskell} 194 | mf <*> mx !$\mathtt{\equiv}$! do f <- mf 195 | !\textcolor{white}{$\mathtt{\equiv}$}! x <- mx 196 | !\textcolor{white}{$\mathtt{\equiv}$}! pure (f x) 197 | \end{haskell} 198 | \end{itemize} 199 | \end{frame} 200 | 201 | \begin{frame}[fragile] 202 | \frametitle{Общая форма \haskinline|do|-нотации} 203 | \begin{itemize} 204 | \item Каждая строка \haskinline|do|-блока имеет вид \haskinline|образец <- м_выражение|, \haskinline|let образец = выражение| или просто \haskinline|м_выражение|. 205 | \item Первые два вида не могут быть в конце. 206 | \item \haskinline|м_выражение| должно иметь тип \haskinline|m a| для какой-то монады \haskinline|m| и типа \haskinline|a|. 207 | \item \haskinline|m| одна для всех строк, \haskinline|a| могут различаться. 208 | \item \haskinline|образец| в строке с \haskinline|<-| имеет тип \haskinline|a|. 209 | \item Если \haskinline|m| "--- экземпляр \haskinline|MonadFail|, то \haskinline|образец| может не быть обязательным для всех значений типа \haskinline|a|, например \haskinline|Just x <- pure Nothing|. 210 | \item Подробности в документации \haskinline|MonadFail|, но у нас в курсе такой необходимости нет. 211 | \end{itemize} 212 | \end{frame} 213 | 214 | \begin{frame}[fragile] 215 | \frametitle{Функции над произвольными монадами} 216 | \begin{itemize}[<+->] 217 | \item Кроме уже виденных \haskinline|=<<| и \haskinline|>=>|, в \haskinline|Prelude| и \haskinline|Control.Monad| есть ещё функции, которые работают для любых монад (или аппликативов). 218 | \item \haskinline|join :: Monad m => m (m a) -> m a|. Эту функцию можно было бы взять как базовую и выразить \haskinline|>>=| через неё. 219 | \item \haskinline|sequence :: Monad m => [m a] -> m [a]|. На самом деле, в библиотеке более общий вариант. 220 | \item \haskinline[fontsize=\small]|mapM :: Monad m => (a -> m b) -> [a] -> m [b]| 221 | \item 222 | \begin{haskell} 223 | zipWithM :: Applicative m => 224 | (a -> b -> m c) -> [a] -> [b] -> m [c] 225 | \end{haskell} 226 | \item Подставьте конкретные монады (например, \haskinline|Maybe| и \haskinline|[]|) и подумайте, что функции будут делать для них. 227 | \end{itemize} 228 | \end{frame} 229 | 230 | \begin{frame}[fragile] 231 | \frametitle{Монада \haskinline|State|} 232 | \begin{itemize} 233 | \item Рассмотрим ещё один, более сложный пример: 234 | \item 235 | \begin{haskellsmall} 236 | newtype State s a = State { runState :: s -> (a, s) } 237 | \end{haskellsmall} 238 | Это \enquote{вычисления с состоянием}, которые могут выдать результат, зависящий от состояния и изменить это состояние. 239 | \item Как сделать их аппликативным функтором? 240 | \begin{haskellsmall} 241 | instance Functor (State s) where 242 | fmap f (State gx) = State (\s1 ->!\pause! 243 | let (x, s2) = gx s1 244 | in!\pause! (f x, s2)) 245 | 246 | instance Applicative (State s) where 247 | pure x = State (\s1 ->!\pause! (x, s1)) 248 | (State gf) <*> (State gx) = State (\s1 -> 249 | let (f, s2) = gf s1!\pause! 250 | (x, s3) = gx s2 251 | in!\pause! (f x, s3)) 252 | \end{haskellsmall} 253 | \end{itemize} 254 | \end{frame} 255 | 256 | \begin{frame}[fragile] 257 | \frametitle{Монада \haskinline|State|} 258 | \begin{itemize} 259 | \item А теперь монадой: 260 | \begin{haskellsmall} 261 | instance Monad (State s) where 262 | State gx >>= f = State (\s1 -> 263 | let (x, s2) = gx s1!\pause! 264 | State gy = f x 265 | in!\pause! gy s2) 266 | \end{haskellsmall} 267 | \pause 268 | \item Определим вспомогательные функции: 269 | \begin{haskellsmall} 270 | get :: State s s 271 | get = State!\pause! (\s -> (s, s)) 272 | 273 | put :: s -> State s () 274 | put x = State!\pause! (\_ -> ((), x)) 275 | 276 | modify :: (s -> s) -> State s () 277 | modify f = do x <- get 278 | put (f x) 279 | \end{haskellsmall} 280 | \end{itemize} 281 | \end{frame} 282 | 283 | \begin{frame}[fragile] 284 | \frametitle{Дополнительное чтение} 285 | \begin{itemize} 286 | \item \href{https://www.snoyman.com/blog/2017/01/functors-applicatives-and-monads}{Functors, Applicatives, and Monads} 287 | \item \href{https://wiki.haskell.org/Typeclassopedia}{Typeclassopedia} (ещё раз) 288 | \item \href{http://dev.stephendiehl.com/hask/#monads}{What I Wish I Knew When Learning Haskell: Monads} 289 | \item \href{https://wiki.haskell.org/All_About_Monads}{All About Monads} 290 | \item И две более сложные монады: 291 | \begin{itemize} 292 | \item \href{http://blog.sigfpe.com/2008/12/mother-of-all-monads.html}{The Mother of all Monads} 293 | \item \href{https://lukepalmer.wordpress.com/2008/08/10/mindfuck-the-reverse-state-monad/}{Mindfuck: The Reverse State Monad} 294 | \item \href{https://tech-blog.capital-match.com/posts/5-the-reverse-state-monad.html}{The Curious Time-Traveling Reverse State Monad} 295 | \end{itemize} 296 | \end{itemize} 297 | \end{frame} 298 | 299 | \end{document} 300 | -------------------------------------------------------------------------------- /lecture11_io.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexeyr/miet-haskell-course/2d8def951630ff403c8843e7c115e8bb100c3080/lecture11_io.pdf -------------------------------------------------------------------------------- /lecture11_io.tex: -------------------------------------------------------------------------------- 1 | % !TeX document-id = {40e7f911-37dd-4856-a048-b5b803f1756a} 2 | % !TeX TXS-program:compile = txs:///pdflatex/[-shell-escape] 3 | 4 | \documentclass[11pt]{beamer} 5 | \input{lecture_preamble.tex} 6 | 7 | \title{Лекция 11: монада IO и взаимодействие с внешним миром} 8 | 9 | \begin{document} 10 | 11 | \begin{frame}[plain] 12 | \maketitle 13 | \end{frame} 14 | 15 | \begin{frame}[fragile] 16 | \frametitle{Монада IO} 17 | \begin{itemize} 18 | \item Среди многих монад стандартной библиотеки, \haskinline|IO| играет особую роль. 19 | \item Значение типа \haskinline|IO a| описывает вычисление с результатом типа \haskinline|a| и побочными эффектами. 20 | \item В первую очередь имеются в виду ввод-вывод, как видно из названия, но также изменяемые переменные и т.д. 21 | \end{itemize} 22 | \end{frame} 23 | 24 | \begin{frame}[fragile] 25 | \frametitle{Программы командной строки} 26 | \begin{itemize} 27 | \item Исполняемая программа в Haskell задаётся значением 28 | \haskinline|main :: IO a| (почти всегда \haskinline|IO ()|). 29 | \item Это аналог \mintinline{c}|int main(int argc, char** argv)| в C, \mintinline{java}|public static void main(char[] args)| в Java и т.д. 30 | \item Соответственно, есть доступ к аргументам командной строки и возможность указать код выхода: 31 | \item \haskinline|System.Environment.getArgs :: IO [String]|. Там же есть функции для чтения переменных окружения. 32 | \item \haskinline|System.Exit.exitWith :: ExitCode -> IO a|. 33 | \end{itemize} 34 | \end{frame} 35 | 36 | \begin{frame}[fragile] 37 | \frametitle{Интерактивные программы} 38 | \begin{itemize} 39 | \item Из консоли можно читать текст, вводимый пользователем: 40 | \begin{haskell} 41 | getLine ::!\pause! IO String 42 | \end{haskell} 43 | \item И писать в неё: 44 | \begin{haskell} 45 | putStr, putStrLn ::!\pause! String -> IO () 46 | print :: Show a => a -> IO () 47 | \end{haskell} 48 | \pause 49 | \item Простой пример: читаем любую строку, выводим её длину и повторяем (а на \haskinline|"quit"| выходим): 50 | \pause 51 | \begin{haskellsmall} 52 | main = do 53 | line <- getLine 54 | if line /= "quit" 55 | then do 56 | print (length line) 57 | main 58 | else 59 | pure () -- или exitSuccess 60 | \end{haskellsmall} 61 | \end{itemize} 62 | \end{frame} 63 | 64 | \begin{frame}[fragile] 65 | \frametitle{Работа с файлами} 66 | \begin{itemize} 67 | \item Модуль \href{http://hackage.haskell.org/package/base/docs/System-IO.html}{\haskinline|System.IO|}. 68 | \item Напомню, \haskinline|type FilePath = String|. 69 | \item Простейшие операции это чтение и запись в файл: 70 | \begin{haskell} 71 | readFile :: FilePath -> IO String 72 | writeFile, appendFile :: 73 | FilePath -> String -> IO () 74 | \end{haskell} 75 | \item 76 | Или можно открыть файл один раз, получить \haskinline|Handle| (дескриптор файла) и работать с ним: 77 | \begin{haskell} 78 | openFile, openBinaryFile :: 79 | FilePath -> IOMode -> IO Handle 80 | hPutStrLn :: Handle -> String -> IO () 81 | hGetStr... 82 | \end{haskell} 83 | \pause 84 | \item 85 | Функции работы с консолью сводятся к этим: 86 | \begin{haskell} 87 | getLine = hGetLine stdin 88 | \end{haskell} 89 | \end{itemize} 90 | \end{frame} 91 | 92 | \begin{frame}[fragile] 93 | \frametitle{Работа с файлами (2)} 94 | \begin{itemize} 95 | \item В чём проблема с кодом вроде 96 | \begin{haskell} 97 | do 98 | file <- openFile path ReadWriteMode 99 | ... 100 | hClose file 101 | \end{haskell} 102 | \pause 103 | \item Что, если на каком-то из шагов случится исключение? Мы остаёмся с открытым файлом. 104 | \item Если это случится много раз, программа вылетит. 105 | \pause 106 | \item Чтобы закрыть файл и при исключении: 107 | \begin{haskell} 108 | withFile path ReadWriteMode $ \file -> do 109 | ... 110 | \end{haskell} 111 | \item Это частный случай функции \haskinline|bracket|. 112 | \end{itemize} 113 | \end{frame} 114 | 115 | \begin{frame}[fragile] 116 | \frametitle{Ленивый ввод-вывод} 117 | \begin{itemize} 118 | \item Функция \haskinline|hGetContents| не читает содержимое файла, а сразу возвращает строку, по мере доступа к которой читается файл. 119 | \item С одной стороны, это хорошо: не нужно явно указывать, сколько читать. 120 | \item Но если закрыть файл до того, как он реально прочитан, строка закончится как если бы она дошла до конца файла: 121 | \begin{haskellsmall} 122 | wrong = do 123 | fileData <- withFile "test.txt" ReadMode hGetContents 124 | putStr fileData 125 | \end{haskellsmall} 126 | \item Нужно использовать данные внутри \haskinline|withFile|: 127 | \begin{haskellsmall} 128 | right = withFile "test.txt" ReadMode $ \file -> do 129 | fileData <- hGetContents file 130 | putStr fileData 131 | \end{haskellsmall} 132 | \end{itemize} 133 | \end{frame} 134 | 135 | \begin{frame}[fragile] 136 | \frametitle{\haskinline|ByteString| и \haskinline|Text|} 137 | \begin{itemize} 138 | \item Мы знаем, что \haskinline|String| занимает очень много места в памяти, что ухудшает и время работы. 139 | \item Вместо него есть \haskinline|Data.ByteString.ByteString|, по сути представляющий собой массив байтов, и \haskinline|Data.ByteString.Lazy.ByteString| как ленивый список таких массивов. 140 | \item Многие функции для работы с ними называются так же, как для \haskinline|String|, но живут в соответствующих модулях (поэтому используется \haskinline|import qualified|). 141 | \item Они годятся для бинарных данных (или ASCII, с помощью \haskinline|Data.ByteString[.Lazy].Char8|). 142 | \item Для текста аналогичный пакет \haskinline|text| и типы в модулях \haskinline|Data.Text[.Lazy]|. 143 | \end{itemize} 144 | \end{frame} 145 | 146 | \begin{frame}[fragile] 147 | \frametitle{Случайные значения} 148 | \begin{itemize} 149 | \item Модуль \haskinline|System.Random| содержит класс 150 | \item 151 | \begin{haskellsmall} 152 | class RandomGen g where 153 | next :: g -> (Int, g) 154 | split :: g -> (g, g) 155 | genRange :: g -> (Int, Int) 156 | genRange _ = (minBound, maxBound) 157 | \end{haskellsmall} 158 | описывающий генераторы случайных \haskinline|Int|. 159 | \item Есть тип \haskinline|StdGen| и \haskinline|instance RandomGen StdGen|. 160 | \item Глобальный генератор живёт в \haskinline|IORef|: 161 | \begin{haskellsmall} 162 | getStdGen :: IO StdGen 163 | setStdGen :: StdGen -> IO () 164 | newStdGen :: IO StdGen -- применяет split к getStdGen 165 | getStdRandom :: (StdGen -> (a, StdGen)) -> IO a 166 | \end{haskellsmall} 167 | \item Типы \haskinline|next| и \haskinline|getStdRandom| могут напомнить про \haskinline|State| и не зря! 168 | \item Реализуем \haskinline|newStdGen| и \haskinline|getStdRandom|. 169 | \end{itemize} 170 | \end{frame} 171 | 172 | \begin{frame}[fragile] 173 | \frametitle{Случайные значения (ответ)} 174 | \begin{itemize} 175 | \item 176 | \begin{haskell} 177 | newStdGen = do 178 | currGen <- getStdGen 179 | let (newGen1, newGen2) = split currGen 180 | setStdGen newGen2 181 | pure newGen1 182 | \end{haskell} 183 | \pause 184 | \item 185 | \begin{haskell} 186 | getStdRandom f = do 187 | currGen <- getStdGen 188 | (result, newGen) = f currGen 189 | setStdGen newGen 190 | pure result 191 | \end{haskell} 192 | \pause 193 | \item Можем выразить одно через другое? 194 | \pause 195 | \begin{haskell} 196 | newStdGen = getStdRandom split 197 | \end{haskell} 198 | \end{itemize} 199 | \end{frame} 200 | 201 | \begin{frame}[fragile] 202 | \frametitle{Случайные значения (2)} 203 | \begin{itemize} 204 | \item Ещё один класс в \haskinline|System.Random| описывает типы, для которых можно получить случайные значения, если есть ГСЧ: 205 | \item 206 | \begin{haskell} 207 | class Random a where 208 | randomR :: RandomGen g => (a, a) -> g -> (a, g) 209 | random :: RandomGen g => g -> (a, g) 210 | \end{haskell} 211 | Первый аргумент \haskinline|randomR|: диапазон, в котором берутся значения. 212 | \item Опять видим тип, похожий на \haskinline|State|. 213 | \item 214 | \begin{haskell} 215 | randomRIO :: Random a => (a, a) -> IO a 216 | randomIO :: Random a => IO a 217 | \end{haskell} 218 | используют глобальный генератор. 219 | \end{itemize} 220 | \end{frame} 221 | 222 | \begin{frame}[fragile] 223 | \frametitle{Побег из \haskinline|IO|} 224 | \begin{itemize} 225 | \item Если у нас есть значение типа \haskinline|IO a|, можно ли превратить его просто в \haskinline|a|? 226 | \pause 227 | \item На самом деле есть функция \haskinline|unsafePerformIO :: IO a -> a|. 228 | \item Но само название говорит о её небезопасности. 229 | \item Конкретное использование может быть и безопасным, но для этого надо знать довольно много о внутренностях Haskell. 230 | \pause 231 | \item Поэтому в рамках этого курса мы её использовать не будем, как и другие \haskinline|unsafe*IO|. 232 | \item А есть ещё \haskinline|accursedUnutterablePerformIO|\ldots 233 | \end{itemize} 234 | \end{frame} 235 | 236 | % TODO добавить ссылки 237 | % \begin{frame}[fragile] 238 | % \frametitle{Дополнительное чтение} 239 | % \begin{itemize} 240 | % \item TODO 241 | % \end{itemize} 242 | % \end{frame} 243 | 244 | \end{document} 245 | -------------------------------------------------------------------------------- /lecture12_property_testing.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexeyr/miet-haskell-course/2d8def951630ff403c8843e7c115e8bb100c3080/lecture12_property_testing.pdf -------------------------------------------------------------------------------- /lecture12_property_testing.tex: -------------------------------------------------------------------------------- 1 | \documentclass[11pt]{beamer} 2 | \input{lecture_preamble.tex} 3 | 4 | \title{Лекция 12: тестирование свойств} 5 | 6 | \begin{document} 7 | \begin{frame}[plain] 8 | \maketitle 9 | \end{frame} 10 | 11 | \begin{frame}[fragile] 12 | \frametitle{Привычный подход к тестированию} 13 | \begin{itemize} 14 | \item Как обычно мы тестируем написанные функции? 15 | \pause 16 | \item Вычисляем функцию на каких-то конкретных аргументах и сравниваем результат с ожидаемым. 17 | \item Плюсы: 18 | \pause 19 | \begin{itemize} 20 | \item Тесты легко понять. 21 | \item Можно сделать даже без помощи библиотек. 22 | \item Но при этом библиотеки для написания таких тестов есть практически для всех языков. 23 | \item Для чистых функций (в Haskell почти для всех!) кроме аргументов и результата функции проверять и нечего. 24 | \end{itemize} 25 | \pause 26 | \item TDD (Test Driven Development) \pause "--- сначала тесты, потом реализация. 27 | \end{itemize} 28 | \end{frame} 29 | 30 | \begin{frame}[fragile] 31 | \frametitle{Недостатки этого подхода} 32 | \begin{itemize} 33 | \item А какие у этого подхода минусы? 34 | \pause 35 | \item Сколько нужно тестов? Всегда можно добавить ещё. 36 | \pause 37 | Разве что если функция на \haskinline|Bool|... 38 | \pause 39 | \item Нужно искать пограничные случаи (т.е. те, в которых ошибка более вероятна) самому. \\ 40 | Хотя есть часто встречающиеся: \pause \haskinline|0|, пустой список, \haskinline|INT_MIN|... 41 | \pause 42 | \item Для каких-то аргументов мы можем не знать правильного результата (и не иметь независимого от тестируемой функции способа его найти). 43 | \pause 44 | \item Есть ситуации, когда программу или функцию в ней намеренно пытаются сломать. \pause Например, злобный преподаватель. 45 | \end{itemize} 46 | \end{frame} 47 | 48 | \begin{frame}[fragile] 49 | \frametitle{Свойства вместо примеров} 50 | \begin{itemize} 51 | \item Тестирование свойств (property testing или property-based testing) позволяет избежать этих проблем (и создать новые, как обычно). 52 | \item Основная идея: вместо результата на конкретных аргументах мы описываем свойства, которые должны выполняться \emph{для всех аргументов} подходящего типа. \pause Или не совсем для всех, но об этом потом. 53 | \pause 54 | \item Эти свойства проверяем на случайных аргументах (скажем, 100 за раз по умолчанию). 55 | \pause 56 | \item В процессе разработки тесты запускаются далеко не один раз, так что каждое свойство будет проверено в сотнях или тысячах случаев. 57 | \end{itemize} 58 | \end{frame} 59 | 60 | % TODO: 1. Перейти на Hedgehog; 61 | % TODO: 2. Добавить примеры неверных свойств без исключений! 62 | 63 | \begin{frame}[fragile] 64 | \frametitle{Простейший пример} 65 | \begin{itemize} 66 | \item Для Haskell две основных библиотеки: \href{http://hackage.haskell.org/package/QuickCheck}{QuickCheck} и \href{https://github.com/hedgehogqa/haskell-hedgehog/}{Hedgehog}. Мы используем первую для простоты кода. 67 | \begin{haskell} 68 | ghci> import Test.QuickCheck 69 | ghci> quickCheck (\x -> (x :: Integer) * x >= 0) 70 | \end{haskell} 71 | \begin{ghci} 72 | +++ OK, passed 100 tests. 73 | \end{ghci} 74 | \item Проверили, что квадраты \haskinline|Integer| неотрицательны. 75 | \end{itemize} 76 | \end{frame} 77 | 78 | \begin{frame}[fragile] 79 | \frametitle{Ещё один пример} 80 | \begin{itemize} 81 | \item Верно ли, что первый элемент списка всегда равен последнему элементу развёрнутого? 82 | \begin{haskellsmall} 83 | ghci> quickCheck (\xs -> head xs === last (reverse xs)) 84 | \end{haskellsmall} 85 | \pause 86 | \begin{ghcitiny} 87 | *** Failed! Exception: 'Prelude.head: empty list' (after 1 test): 88 | [] 89 | \end{ghcitiny} 90 | \item Как исправить? \pause Нужно добавить условие, что список непустой: 91 | \begin{haskellsmall} 92 | ghci> quickCheck (\xs -> not (null xs) ==> 93 | head xs === last (reverse xs)) 94 | \end{haskellsmall} 95 | \pause 96 | \begin{ghcismall} 97 | +++ OK, passed 100 tests. 98 | \end{ghcismall} 99 | 100 | \end{itemize} 101 | \end{frame} 102 | 103 | \begin{frame}[fragile] 104 | \frametitle{Основные типы и функции} 105 | \begin{itemize} 106 | \item \haskinline|Gen a|: генератор случайных значений типа \haskinline|a|. 107 | \item \haskinline|forAll :: (Show a, Testable prop) => Gen a -> (a -> prop) -> Property|: создаёт свойство с использованием заданного генератора. 108 | \item \haskinline|class Arbitrary a where arbitrary :: Gen a|: типы, у которых есть \enquote{стандартный генератор}. 109 | \item[] 110 | \item \haskinline|class Testable a|: типы, которые можно протестировать (например \haskinline|Bool| и \haskinline|Property|). 111 | \item Функции \haskinline|a -> prop|, где \haskinline|Arbitrary a|, \haskinline|Show a|, \haskinline|Testable prop| "--- тоже \haskinline|Testable|. 112 | \item \haskinline|quickCheck, verboseCheck :: Testable prop => prop -> IO ()|: проверяет свойство и выводит результат. 113 | \end{itemize} 114 | \end{frame} 115 | 116 | \begin{frame}[fragile] 117 | \frametitle{Генераторы значений} 118 | \begin{itemize} 119 | \item Примеры базовых генераторов: 120 | \begin{haskell} 121 | choose :: Random a => (a, a) -> Gen a 122 | elements :: [a] -> Gen a 123 | \end{haskell} 124 | \item Из простых генераторов можно строить более сложные, пользуясь тем, что \haskinline|Gen| "--- монада. Например: 125 | \begin{haskell} 126 | genPair :: Gen a -> Gen b -> Gen (a, b) 127 | genPair ga gb =!\pause! liftA2 (,) ga gb 128 | 129 | genMaybe :: Gen a -> Gen (Maybe a) 130 | genMaybe ga = do is_just <- arbitrary @Bool 131 | if is_just 132 | then!\pause! fmap Just ga 133 | else!\pause! pure Nothing 134 | \end{haskell} 135 | \end{itemize} 136 | \end{frame} 137 | 138 | \begin{frame}[fragile] 139 | \frametitle{Генераторы значений} 140 | \begin{itemize} 141 | \item С рекурсивными типами несколько сложнее. Можно попробовать привести пример 142 | \begin{haskelltiny} 143 | data Tree a = Empty | Node a (Tree a) (Tree a) 144 | 145 | treeOf :: Gen a -> Gen (Tree a) 146 | treeOf g = oneOf [pure Empty, 147 | liftA3 Node g (treeOf g) (treeOf g)] 148 | \end{haskelltiny} 149 | но это определение плохое: оно даёт очень маленькие деревья (почему?). 150 | \item Правильнее: 151 | \begin{haskelltiny} 152 | treeOf g = do n <- getSize 153 | if n == 0 154 | then pure Empty 155 | else do 156 | k <- choose (0,n) 157 | liftA3 Node g (resize k $ treeOf g) 158 | (resize (n-k) $ treeOf g) 159 | \end{haskelltiny} 160 | \end{itemize} 161 | \end{frame} 162 | 163 | \begin{frame}[fragile] 164 | \frametitle{Минимизация контрпримеров} 165 | \begin{itemize} 166 | \item Найденный сначала контрпример обычно будет довольно большим (хотя начинается генерация с примеров малого размера). 167 | \item Поэтому библиотека старается найти и выдать пользователю более простые значения аргументов, дающие ту же ошибку. 168 | \item В случае QuickCheck за это уменьшение отвечает функция \haskinline|shrink :: a -> [a]| в классе \haskinline|Arbitrary|. 169 | \item В Hedgehog, это уменьшение задаётся как часть \haskinline|Hedgehog.Gen|, что имеет преимущества: 170 | \begin{itemize} 171 | \item Если что-то сгенерировали, точно можно уменьшить. 172 | \item При уменьшении сохраняются инварианты. 173 | \item Подробнее: \href{https://hypothesis.works/articles/integrated-shrinking/}{Integrated vs type based shrinking}. 174 | \end{itemize} 175 | \end{itemize} 176 | \end{frame} 177 | 178 | \begin{frame}[fragile] 179 | \frametitle{Воспроизводимость} 180 | \begin{itemize} 181 | \item Если мы нашли контрпример (например, с десятого запуска) и изменили реализацию, нужно проверить, что этот случай исправился. 182 | \item В QuickCheck для этого просто запускаем то же свойство с напечатанными аргументами. Можно также добавить обычный тест с ними. 183 | \item Конечно, это удобно делать только тогда, когда минимизация более-менее удалась. 184 | \item Кроме того, для некоторых типов результат \haskinline|show x| недостаточен для точного воспроизведения \haskinline|x| (придумаете пример?) 185 | \item Поэтому в Hedgehog кроме аргументов выдаётся параметр для перезапуска генератора. 186 | \end{itemize} 187 | \end{frame} 188 | 189 | \begin{frame}[fragile] 190 | \frametitle{Реализация Arbitrary для своих типов} 191 | \begin{itemize} 192 | \item Для пар: 193 | \begin{haskell} 194 | instance (Arbitrary a, Arbitrary b) => Arbitrary (a, b) where 195 | arbitrary = genPair arbitrary arbitrary 196 | shrink (x, y) = [(x', y') | x' <- shrink x, y' <- shrink y] 197 | \end{haskell} 198 | \item Для деревьев: 199 | \begin{haskell} 200 | instance Arbitrary a => Arbitrary (Tree a) where 201 | arbitrary = treeOf arbitrary 202 | shrink Empty = [] 203 | shrink (Node x l r) = 204 | [Empty, l, r] ++ 205 | [Node x' l' r' | 206 | (x', l', r') <- shrink (x, l, r)] 207 | \end{haskell} 208 | \end{itemize} 209 | \end{frame} 210 | 211 | \begin{frame}[fragile] 212 | \frametitle{Часто встречающиеся виды свойств} 213 | \begin{itemize} 214 | \item Самое тривиальное свойство "--- функция даёт результат, а не выкидывает исключение. 215 | \item Сверка с другой реализацией той же функции. 216 | \item При наличии также обратной функции: \haskinline|encode (decode x) == x|. 217 | \item Алгебраические свойства: идемпотентность, коммутативность, ассоциативность и так далее. 218 | \item Законы классов типов. 219 | \end{itemize} 220 | \end{frame} 221 | 222 | \begin{frame}[fragile] 223 | \frametitle{Генераторы функций} 224 | \begin{itemize} 225 | \item Для тестирования функций высшего порядка нужно уметь генерировать их аргументы, то есть случайные функции. Для этого в QuickCheck есть \href{http://hackage.haskell.org/package/QuickCheck-2.13.1/docs/Test-QuickCheck.html#g:14}{тип \haskinline|Fun| и класс \haskinline|Function|}. \haskinline|Fun a b| представляет функцию \haskinline|a -> b| как набор пар аргумент-значение и значение по умолчанию (например \haskinline|{"elephant"->1, "monkey"->1, _->0}|). Экземпляр \haskinline|Function a| нужен для генерации \haskinline|Fun a b|. 226 | \end{itemize} 227 | \end{frame} 228 | 229 | \begin{frame}[fragile] 230 | \frametitle{Свойства функций} 231 | \begin{itemize} 232 | \item Как пример использования, покажем, что функции \haskinline|Int -> String| "--- чистые, то есть дают одинаковый результат при повторном вызове: 233 | \begin{haskell} 234 | prop :: Fun Int String -> Int -> Bool 235 | prop (Fn f) x = f x == f x 236 | ghci> quickCheck prop 237 | \end{haskell} 238 | \begin{ghci} 239 | +++ OK, passed 100 tests. 240 | \end{ghci} 241 | \end{itemize} 242 | \end{frame} 243 | 244 | \begin{frame}[fragile] 245 | \frametitle{Тестирование законов классов} 246 | \begin{itemize} 247 | \item Библиотека \hackage{quickcheck-classes} содержит свойства, проверяющие законы для множества классов. 248 | \item Например, законы \haskinline|Eq| определяются функцией \haskinline|eqLaws :: (Eq a, Arbitrary a, Show a) => Proxy a -> Laws| и могут быть проверены для \haskinline|Rational| с помощью \haskinline|lawsCheck (eqLaws (Proxy @Rational)))|. 249 | \item Для проверки законов \haskinline|Functor/Applicative/Monad/...| вместо \haskinline|Proxy| нужно \haskinline|Proxy1|. 250 | \item Для Hedgehog совершенно аналогичная \hackage{hedgehog-classes} (только последняя версия у меня не поставилась). 251 | \end{itemize} 252 | \end{frame} 253 | 254 | \begin{frame}[fragile] 255 | \frametitle{Ещё раз: Hedgehog и QuickCheck} 256 | \begin{itemize} 257 | \item Главная разница: в Hedgehog уменьшение интегрировано с генерацией значений, а не определяется типом. 258 | \item Нет \haskinline|Arbitrary|, генераторы контролируются полностью явно. 259 | \item Для улучшения воспроизводимости вместе с контрпримером выдаётся seed. 260 | \item Улучшенный показ контрпримеров с помощью пакета \hackage{pretty-show} и функции \href{http://hackage.haskell.org/package/hedgehog-1.0/docs/Hedgehog.html#v:diff}{diff}. 261 | \item Проще работа с монадическими свойствами. 262 | \item Поддержка тестирования с состоянием. 263 | \item Для генерации функций нужна отдельная библиотека \hackage{hedgehog-fn}. 264 | \item Параллельная проверка (для ускорения). 265 | \end{itemize} 266 | \end{frame} 267 | 268 | \begin{frame}[fragile] 269 | \frametitle{Дополнительное чтение} 270 | \begin{itemize} 271 | \item \href{https://hypothesis.works/articles/quickcheck-in-every-language/}{QuickCheck in Every Language} Список библиотек тестирования свойств на разных языках на апрель 2016 (от автора Hypothesis для Python). 272 | \item \href{https://begriffs.com/posts/2017-01-14-design-use-quickcheck.html}{The Design and Use of QuickCheck} 273 | \item \href{https://teh.id.au/posts/2017/04/23/property-testing-with-hedgehog/}{Property testing with Hedgehog} 274 | \item \href{http://www.well-typed.com/blog/2019/05/integrated-shrinking/}{Integrated versus Manual Shrinking} Реализация и сравнение мини-версий QuickCheck и Hedgehog. 275 | \item \href{https://habr.com/ru/post/434008/}{Как перестать беспокоиться и начать писать тесты на основе свойств} 276 | \item \href{https://www.hillelwayne.com/post/contract-examples/}{Finding Property Tests} 277 | \item \href{https://wickstrom.tech/programming/2019/03/02/property-based-testing-in-a-screencast-editor-introduction.html}{Property-Based Testing in a Screencast Editor} 278 | \item \href{https://austinrochford.com/posts/2014-05-27-quickcheck-laws.html}{Verifying Typeclass Laws in Haskell with QuickCheck} 279 | \item \href{https://blog.jrheard.com/hypothesis-and-pexpect}{Using Hypothesis and Pexpect to Test High School Programming Assignments} 280 | \end{itemize} 281 | \end{frame} 282 | 283 | \end{document} 284 | -------------------------------------------------------------------------------- /lecture13_concurrency.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexeyr/miet-haskell-course/2d8def951630ff403c8843e7c115e8bb100c3080/lecture13_concurrency.pdf -------------------------------------------------------------------------------- /lecture13_concurrency.tex: -------------------------------------------------------------------------------- 1 | % !TeX document-id = {efee450f-6069-4a1d-a54f-d3ae0eb7270d} 2 | % !TeX TXS-program:compile = txs:///pdflatex/[-shell-escape] 3 | 4 | \documentclass[11pt]{beamer} 5 | \input{lecture_preamble.tex} 6 | 7 | \title{Лекция 13: многопоточность} 8 | 9 | \begin{document} 10 | \begin{frame}[plain] 11 | \maketitle 12 | \end{frame} 13 | 14 | \begin{frame}[fragile] 15 | \frametitle{Два вида многопоточности: конкурентность и параллелизм} 16 | \begin{itemize} 17 | \item Все программы, которые мы писали до сих пор, были однопоточными. 18 | \item У современных компьютеров много ядер, мы задействуем только одно. 19 | \item Чтобы использовать их, нужна многопоточность. \pause 20 | \item Параллелизм: физическая многопоточность. Много ядер (или других ресурсов) используются для ускорения выполнения одной задачи. \pause 21 | \item Конкурентность: логическая многопоточность. Несколько задач, которые могут выполняться логически одновременно. А значит, потенциально и физически одновременно. 22 | \end{itemize} 23 | \end{frame} 24 | 25 | \begin{frame}[fragile] 26 | \frametitle{Детерминированность} 27 | \begin{itemize} 28 | \item Модель программирования детерминирована, если результат программы зависит только от полученных данных (аргументов и ввода пользователя). \pause 29 | \item Недетерминирована, если результат зависит от того, в каком порядке произойдут события (или от сгенерированных случайных чисел и т.д.). \pause 30 | \item Обычно параллельные программы детерминированы, конкурентные нет. 31 | \item Недетерминированность усложняет тестирование, отладку и понимание программ, но часто без неё не обойтись. 32 | \item Тестирование свойств это пример пользы от недетерминированности. 33 | \end{itemize} 34 | \end{frame} 35 | 36 | \begin{frame}[fragile] 37 | \frametitle{Параллелизм: монада \haskinline|Eval|} 38 | \begin{itemize} 39 | \item Модуль \haskinline|Concurrent.Parallel.Strategies| в пакете \hackage{parallel}. 40 | \item Тип \haskinline|Eval a| --- монада, где в \haskinline|mx >>= f| значение \haskinline|mx| будет вычислено до СЗНФ перед запуском \haskinline|f|. \pause 41 | \item Есть функции 42 | \begin{haskell} 43 | runEval :: Eval a -> a 44 | rpar :: a -> Eval a 45 | rseq :: a -> Eval a 46 | parEval :: Eval a -> Eval a 47 | \end{haskell} 48 | \item \haskinline|rpar x|: запустить вычисление \haskinline|x| и сразу вернуться. 49 | \item \haskinline|rseq x|: вернуться после вычисления \haskinline|x|. 50 | \item \haskinline|parEval x|: запустить вычисление, описанное в \haskinline|x|, и сразу вернуться. 51 | \end{itemize} 52 | \end{frame} 53 | 54 | \begin{frame}[fragile] 55 | \frametitle{Примеры \haskinline|Eval|} 56 | \begin{itemize} 57 | \item 58 | Запуск двух вычислений параллельно: 59 | \begin{haskell} 60 | runEval $ do 61 | a <- rpar (f x) 62 | b <- rpar (f y) 63 | return (a, b) 64 | \end{haskell} 65 | % \includegraphics[width=\textwidth]{concurrency_eval1.png} 66 | \pause 67 | \item 68 | Запуск двух вычислений параллельно и возврат, когда оба закончатся: 69 | \begin{haskell} 70 | runEval $ do 71 | a <- rpar (f x) 72 | b <- rseq (f y) 73 | rseq a 74 | return (a, b) 75 | \end{haskell} 76 | % \includegraphics[width=\textwidth]{concurrency_eval2.png} 77 | \end{itemize} 78 | \end{frame} 79 | 80 | \begin{frame}[fragile] 81 | \frametitle{Фибоначчи в \haskinline|Eval|} 82 | \begin{itemize} 83 | \item 84 | \begin{haskellsmall} 85 | parFib1 :: Integer -> Integer 86 | parFib1 0 = 1 87 | parFib1 1 = 1 88 | parFib1 n = runEval $ do !\pause! 89 | f1 <- rpar (parFib1 (n - 1)) 90 | f2 <- rseq (parFib1 (n - 2)) 91 | -- rseq, чтобы занять текущий поток, можно и rpar 92 | pure (f1 + f2) 93 | \end{haskellsmall} 94 | \pause 95 | \item 96 | \begin{haskellsmall} 97 | parFib2 :: Integer -> Eval Integer 98 | parFib2 0 = pure 1 99 | parFib2 1 = pure 1 100 | parFib2 n = do !\pause! 101 | f1 <- parEval (parFib2 (n - 1)) 102 | f2 <- parFib2 (n - 2) 103 | pure (f1 + f2) 104 | \end{haskellsmall} 105 | \pause 106 | \item Здесь для простоты кода много повторных вычислений! 107 | \end{itemize} 108 | \end{frame} 109 | 110 | % TODO восстановить слайд 111 | %\begin{frame}[fragile] 112 | %\frametitle{Примеры \haskinline|Eval|} 113 | %\begin{columns} 114 | % \begin{column}{0.5\textwidth} 115 | % Запуск двух вычислений параллельно: 116 | %\begin{haskell} 117 | %runEval $ do 118 | % a <- rpar (f x) 119 | % b <- rpar (f y) 120 | % return (a, b) 121 | %\end{haskell} 122 | %% \includegraphics[width=\textwidth]{concurrency_eval1.png} 123 | % \end{column}\pause 124 | % \begin{column}{0.5\textwidth} 125 | % Запуск двух вычислений параллельно и возврат, когда оба закончатся: 126 | %\begin{haskell} 127 | %runEval $ do 128 | % a <- rpar (f x) 129 | % b <- rseq (f y) 130 | % rseq a 131 | % return (a, b) 132 | %\end{haskell} 133 | %% \includegraphics[width=\textwidth]{concurrency_eval2.png} 134 | % \end{column} 135 | %\end{columns} 136 | %\end{frame} 137 | 138 | \begin{frame}[fragile] 139 | \frametitle{Стратегии вычислений} 140 | \begin{itemize} 141 | \item \haskinline|type Strategy a = a -> Eval a|. 142 | \item \haskinline|rpar| и \haskinline|rseq| --- стратегии. 143 | \item \haskinline|r0| --- стратегия, которая ничего не вычисляет. 144 | \item Стратегия принимает на вход thunk, вычисляет его части с помощью \haskinline|r0|, \haskinline|rpar| и \haskinline|rseq|. 145 | \item Например, обобщая пример с прошлого слайда 146 | \begin{haskell} 147 | rparTuple2 :: Strategy (a, b) 148 | rparTuple2 (a, b) = do !\pause! 149 | a' <- rpar a 150 | b' <- rpar b 151 | return (a', b') 152 | \end{haskell} 153 | \pause 154 | (или \haskinline[fontsize=\small]|rparTuple2 (a, b) = liftA2 (,) (rpar a) (rpar b)|) 155 | \item 156 | \begin{haskell} 157 | withStrategy :: Strategy a -> a -> a 158 | withStrategy s x = runEval (s x) 159 | \end{haskell} 160 | \end{itemize} 161 | \end{frame} 162 | 163 | \begin{frame}[fragile] 164 | \frametitle{Комбинаторы стратегий} 165 | \begin{itemize} 166 | \item Обобщим \haskinline|rparTuple2| дальше, чтобы она принимала стратегии на вход: 167 | \begin{haskell} 168 | evalTuple2 :: Strategy a -> Strategy b -> 169 | Strategy (a, b) 170 | evalTuple2 strat_a strat_b (a, b) = !\pause! 171 | liftA2 (,) (strat_a a) (strat_b b) 172 | \end{haskell} 173 | \pause 174 | \item \haskinline|rparWith :: Strategy a -> Strategy a| запускает стратегию параллельно. 175 | \item Например, \haskinline|rparWith rpar| и \haskinline|rparWith rseq| эквивалентны \haskinline|rpar|. 176 | \pause 177 | \item 178 | \begin{haskell} 179 | parTuple2 :: Strategy a -> Strategy b -> 180 | Strategy (a, b) 181 | parTuple2 strat_a strat_b = 182 | evalTuple2 (rparWith strat_a) (rparWith strat_b) 183 | \end{haskell} 184 | \end{itemize} 185 | \end{frame} 186 | 187 | \begin{frame}[fragile] 188 | \frametitle{Комбинаторы стратегий для списков} 189 | \begin{itemize} 190 | \item С помощью стратегий достаточно легко сделать параллельные \haskinline|map|/\haskinline|filter|/и т.д. 191 | \item Например, используя \haskinline|parList :: Strategy a -> Strategy [a]|: 192 | \begin{haskell} 193 | parallelMap :: (a -> b) -> [a] -> [b] 194 | parallelMap f xs = 195 | withStrategy (parList rseq) (map f xs) 196 | \end{haskell} 197 | \pause 198 | \item Это скорее всего создаст слишком много \enquote{искр}, лучше использовать 199 | \begin{itemize} 200 | \item \haskinline|parListChunk|: разбивает список на части одинаковой длины и работает параллельно с каждой частью 201 | \item \haskinline|parBuffer|: начинает работать параллельно с первыми элементами списка, добавляет следующие при использовании результатов 202 | \end{itemize} 203 | \end{itemize} 204 | \end{frame} 205 | 206 | \begin{frame}[fragile] 207 | \frametitle{Монада \haskinline|Par|: параллелизм без ленивости} 208 | \begin{itemize} 209 | \item Модуль \haskinline|Control.Monad.Par| в пакете \haskinline|monad-par|. 210 | \item Тип \haskinline|Par a| --- тоже монада, описывающая процесс вычисления значения типа \haskinline|a| с параллельными частями. 211 | \item \haskinline|fork :: Par () -> Par ()| создаёт новый поток. 212 | \item \haskinline|runPar :: Par a -> a| производит вычисление и возвращает его результат. \pause 213 | \item Потоки общаются между собой с помощью \haskinline|IVar|, которые можно записать только 1 раз. 214 | \item В результате не должно быть \haskinline|IVar| или ссылок на них! 215 | \item При соблюдении этого условия вычисления в \haskinline|Par| детерминированы. 216 | \end{itemize} 217 | \end{frame} 218 | 219 | \begin{frame}[fragile] 220 | \frametitle{\haskinline|IVar|} 221 | \begin{itemize} 222 | \item Интерфейс \haskinline|IVar|: 223 | \begin{haskell} 224 | new :: Par (IVar a) 225 | newFull :: NFData a => a -> Par (IVar a) 226 | put :: NFData a => IVar a -> a -> Par () 227 | get :: IVar a -> Par a 228 | \end{haskell} 229 | \item Значения, положенные в \haskinline|IVar|, полностью вычисляются (есть ещё \haskinline|newFull_| и \haskinline|put_|). 230 | \item Переменной можно дать значение только 1 раз, иначе исключение. 231 | \item При вызове \haskinline|get| для переменной, ещё не имеющей значения, начинается ожидание. 232 | \end{itemize} 233 | \end{frame} 234 | 235 | \begin{frame}[fragile] 236 | \frametitle{Примеры \haskinline|Par|} 237 | \begin{itemize} 238 | \item \haskinline|spawn :: NFData a => Par a -> Par (IVar a)|: аналог \haskinline|fork|, но возвращающий переменную, в которой будет сохранено вычисленное значение 239 | \begin{haskell} 240 | spawn par = do 241 | var <- new 242 | fork $ do (x <- par; put var x) 243 | return var 244 | \end{haskell} 245 | \item Параллельный \haskinline|map|: 246 | \begin{haskell} 247 | parallelMap :: NFData b => (a -> b) -> [a] -> [b] 248 | parallelMap f xs = runPar $ do 249 | ivars <- mapM (\x -> spawn (pure (f x)) xs 250 | mapM get ivars 251 | \end{haskell} 252 | Как и в прошлый раз, создаёт \enquote{поток} для каждого элемента, для простоты кода. 253 | \end{itemize} 254 | \end{frame} 255 | 256 | \begin{frame}[fragile] 257 | \frametitle{Фибоначчи в \haskinline|Par|} 258 | \begin{itemize} 259 | \begin{haskell} 260 | parFib :: Integer -> Par Integer 261 | parFib 0 = pure 1 262 | parFib 1 = pure 1 263 | parFib n = do !\pause! 264 | f1var <- spawn (parFib (n - 1)) 265 | f2 <- parFib (n - 2) 266 | f1 <- get f1var 267 | pure (f1 + f2) 268 | \end{haskell} 269 | \end{itemize} 270 | \end{frame} 271 | 272 | \begin{frame}[fragile] 273 | \frametitle{Конкурентность} 274 | \begin{itemize} 275 | \item Перейдём от параллелизма к конкурентности. 276 | \item Модуль \haskinline|Control.Concurrent| в пакете \haskinline|base| и \haskinline|C.C.MVar|/\haskinline|C.C.Chan|/\haskinline|C.C.QSem[N]|. 277 | \item \haskinline|ThreadId| --- ссылка на поток. 278 | \item \haskinline|forkIO :: IO () -> IO ThreadId| создаёт новый поток, который произведёт данные действия и выйдет, возвращает ссылку на созданный поток. 279 | \pause 280 | \item Это так называемый \enquote{зелёный поток}, то есть не поток операционной системы, а похожий по поведению объект виртуальной машины Haskell. 281 | \end{itemize} 282 | \end{frame} 283 | 284 | \begin{frame}[fragile] 285 | \frametitle{\haskinline|MVar|} 286 | \begin{itemize} 287 | \item Потоки могут взаимодействать через \haskinline|MVar|, которые очень похожи на \haskinline|IVar|, но записанное значение можно убрать или изменить. 288 | \item Переменная в любой момент или пустая или полная (содержит значение). 289 | \begin{haskellsmall} 290 | newEmptyMVar :: IO (MVar a) 291 | newMVar :: a -> IO (MVar a) 292 | putMVar :: MVar a -> a -> IO () 293 | readMVar :: MVar a -> IO a 294 | takeMVar :: MVar a -> IO a 295 | isEmptyMVar :: MVar a -> IO Bool 296 | \end{haskellsmall} 297 | \item Если \haskinline|putMVar| вызвана на полной переменной, она ждёт, пока та не опустеет. 298 | \item \haskinline|takeMVar| и \haskinline|readMVar| отличаются тем, что первая убирает прочитанное значение. На пустой переменной обе ждут. 299 | \end{itemize} 300 | \end{frame} 301 | 302 | \begin{frame}[fragile] 303 | \frametitle{Фибоначчи с \haskinline|MVar|} 304 | \begin{itemize} 305 | \item Реализация вычисления чисел Фибоначчи с помощью \haskinline|MVar| не сильно отличается от \haskinline|Par| (только аналога \haskinline|spawn| в стандартной библиотеке нет, смотрите пакет \haskinline|async|): 306 | \begin{haskell} 307 | parFib :: Integer -> IO Integer 308 | parFib 0 = pure 1 309 | parFib 1 = pure 1 310 | parFib n = do !\pause! 311 | f1var <- newEmptyMVar 312 | forkIO $ do 313 | f1' <- parFib (n - 1) 314 | putMVar f1var f1' 315 | f2 <- parFib (n - 2) 316 | f1 <- readMVar f1var 317 | pure (f1 + f2) 318 | \end{haskell} 319 | \end{itemize} 320 | \end{frame} 321 | 322 | \begin{frame}[fragile] 323 | \frametitle{Правильный Фибоначчи} 324 | \begin{itemize} 325 | \item Уже упоминалось, что приведённые программы для вычисления чисел Фибоначчи имеют серьёзный недостаток. 326 | \item Например, \haskinline|parFib 4| вызовет параллельно \haskinline|parFib 3| и \haskinline|parFib 2|, а \haskinline|parFib 3| снова вызовет \haskinline|parFib 2|. 327 | \item Чтобы этого избежать, нужно добавить вспомогательный аргумент для хранения уже полученных результатов. 328 | \item Его тип может быть: 329 | \begin{itemize} 330 | \item Для \haskinline|Eval|: \haskinline|Map Integer Integer|. 331 | \item Для \haskinline|Par|: \haskinline|Map Integer (IVar Integer)|. 332 | \item Для \haskinline|IO|: \haskinline|MVar (Map Integer Integer)|, хотя \haskinline|Map Integer (MVar Integer)| тоже подойдёт. 333 | \end{itemize} 334 | \end{itemize} 335 | \end{frame} 336 | 337 | \begin{frame}[fragile] 338 | \frametitle{\haskinline|Chan|} 339 | \begin{itemize} 340 | \item \haskinline|MVar| можно рассматривать как каналы, хранящие не более одного значения. 341 | \item \haskinline|Chan| это канал для произвольного количества значений. 342 | \begin{haskell} 343 | newChan :: IO (Chan a) 344 | writeChan :: Chan a -> a -> IO () 345 | readChan :: Chan a -> IO a 346 | \end{haskell} 347 | \item \haskinline|writeChan| сохраняет значение в конец очереди. 348 | \item \haskinline|readChan| читает из её начала и ждёт, если она пуста. 349 | \end{itemize} 350 | \end{frame} 351 | 352 | \begin{frame}[fragile] 353 | \frametitle{Транзакции} 354 | \begin{itemize} 355 | \item Понятие транзакций может быть знакомо по базам данных. 356 | \item Это наборы операций, которые все вместе либо выполняются успешно, либо откатываются. 357 | \item В Haskell это позволяет делать монада \haskinline|STM| с операциями над \haskinline|TVar|. 358 | \item Типы гарантируют, что у действия в монаде \haskinline|STM| не может быть побочных эффектов, кроме изменения значений \haskinline|TVar|. 359 | \item Обычно проще написать правильный код, который работает с несколькими \haskinline|TVar|, чем с \haskinline|MVar|, так как можно не беспокоиться о видимости промежуточных состояний. 360 | \end{itemize} 361 | \end{frame} 362 | 363 | \begin{frame}[fragile] 364 | \frametitle{Дополнительное чтение} 365 | \begin{itemize} 366 | \item \href{https://simonmar.github.io/pages/pcph.html}{Parallel and Concurrent Programming in Haskell} (книга, 2013, доступна бесплатно) 367 | \item \href{https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/parallel_haskell2.pdf}{A Tutorial on Parallel and Concurrent Programming in Haskell} 368 | \item \href{https://erlang.org/}{Erlang} Функциональный язык, построенный на модели акторов (взаимодействующих только через отправку сообщений друг другу вместо общих переменных). 369 | \item \href{https://stackoverflow.com/questions/23326920/difference-between-par-monad-and-eval-monad-with-deepseq/23428610#23428610}{Ответ на Stack Overflow про разницу между \haskinline|Eval| и \haskinline|Par|} (ещё один в конце главы 4 первой ссылки). 370 | \item \href{https://hackage.haskell.org/package/streamly}{Streamly: Beautiful Streaming, Concurrent and Reactive Composition} 371 | \item \href{https://hackage.haskell.org/package/lvish}{lvish} Пакет, обобщающий \haskinline|Par| на основе теории решёток. К сожалению, мёртв с 2014. 372 | \end{itemize} 373 | \end{frame} 374 | 375 | \end{document} 376 | -------------------------------------------------------------------------------- /lecture14_final.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexeyr/miet-haskell-course/2d8def951630ff403c8843e7c115e8bb100c3080/lecture14_final.pdf -------------------------------------------------------------------------------- /lecture14_final.tex: -------------------------------------------------------------------------------- 1 | % !TeX document-id = {fc72019d-d3ba-4743-9e76-a08bcafbe161} 2 | % !TeX TXS-program:compile = txs:///pdflatex/[-shell-escape] 3 | 4 | \documentclass[11pt]{beamer} 5 | \input{lecture_preamble.tex} 6 | 7 | \title{Лекция 14: что дальше?} 8 | 9 | \begin{document} 10 | \begin{frame}[plain] 11 | \maketitle 12 | \end{frame} 13 | 14 | \begin{frame}[fragile] 15 | \frametitle{Нерассмотренные части Haskell: стандартные библиотеки} 16 | \begin{itemize} 17 | \item В самом начале упоминалось, что тип \haskinline{String} очень плох для практического использования. Альтернативы в пакетах \hackage{text} и \hackage{bytestring}. 18 | \item Коллекции: 19 | \begin{itemize} 20 | \item \hackage{containers}: \haskinline|Data.Map|, \haskinline|Data.Set|, \haskinline|Data.Sequence|. 21 | \item \hackage{unordered-containers}: \haskinline|Data.Hash{Map/Set}|. 22 | \item Массивы: \hackage{array} и \hackage{vector}. 23 | \end{itemize} 24 | \item Комбинаторы парсеров: \hackage{megaparsec}. 25 | \item Сборка проектов и управление зависимостями: \href{https://cabal.readthedocs.io/en/stable/}{cabal} и \href{https://docs.haskellstack.org/en/stable/}{stack}. 26 | \item Линзы (\hackage{lens}): обобщение геттеров и сеттеров. 27 | \item Альтернативы \haskinline|show| для больших структур: \hackage{pretty-show} и \hackage{prettyprinter}. 28 | \end{itemize} 29 | \end{frame} 30 | 31 | \begin{frame}[fragile] 32 | \frametitle{Нерассмотренные части Haskell: язык} 33 | \begin{itemize} 34 | \item \href{https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#type-families}{Семейства типов}. 35 | \item \href{https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#arbitrary-rank-polymorphism}{Типы высших рангов} с \haskinline|forall| \emph{внутри} типа. 36 | \begin{itemize} 37 | \item Важный пример: монада \haskinline|ST|. 38 | \end{itemize} 39 | \item \href{https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#existentially-quantified-data-constructors}{Экзистенциальные типы} (выраженные всё равно через \haskinline|forall|). 40 | \item \href{https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#pattern-synonyms}{Синонимы образцов}. 41 | \item \href{https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#template-haskell}{Template Haskell}: генерация кода во время компиляции. 42 | \begin{itemize} 43 | \item Могли видеть использование в документации QuickCheck/Hedgehog. 44 | \end{itemize} 45 | \item Исключения. 46 | \item Foreign Function Interface для интеграции с C. 47 | \end{itemize} 48 | \end{frame} 49 | 50 | \begin{frame}[fragile] 51 | \frametitle{Подходы к эффектам} 52 | \begin{itemize} 53 | \item Многие монады (и аппликативные функторы) можно рассматривать как побочные эффекты. 54 | \begin{itemize} 55 | \item \haskinline|IO|: понятно. 56 | \item \haskinline|State|: чтение и запись в память. 57 | \item \haskinline|[]|: недетерминированность 58 | \item \ldots 59 | \end{itemize} 60 | \pause 61 | \item Как их комбинировать? 62 | \item Композиция монад не обязательно монада. 63 | \pause 64 | \item Трансформеры монад: \hackage{transformers} и \hackage{mtl} 65 | \item Алгебраические эффекты 66 | \item Свободные (free) и \enquote{более свободные} (freer) монады 67 | \item Небольшой обзор: \href{https://ocharles.org.uk/posts/2016-01-26-transformers-free-monads-mtl-laws.html}{Monad transformers, free monads, mtl, laws and a new approach} 68 | \end{itemize} 69 | \end{frame} 70 | 71 | \begin{frame}[fragile] 72 | \frametitle{Функциональные структуры данных} 73 | \begin{itemize} 74 | \item Функциональные структуры данных очень отличаются от привычных! 75 | \item Классическая книга: Okasaki, Purely functional data structures 76 | \item \href{https://cstheory.stackexchange.com/questions/1539/whats-new-in-purely-functional-data-structures-since-okasaki}{Список \enquote{после Окасаки} (2010)}. И \href{https://www.reddit.com/r/haskell/comments/a4wk1i/seminal_works_after_purely_functional_data/ebm30ra/}{ещё немного}. 77 | \pause 78 | \item Функциональные структуры, позволяющие доступ к своим старым состояниям, полезны и в императивных языках. 79 | \begin{itemize} 80 | \item А изменяемые "--- в Haskell через \haskinline|IO| или \haskinline|ST|. 81 | \end{itemize} 82 | \end{itemize} 83 | \end{frame} 84 | 85 | \begin{frame}[fragile] 86 | \frametitle{Будущее Haskell} 87 | \begin{itemize} 88 | \item \href{https://www.tweag.io/posts/2017-03-13-linear-types.html}{Линейные типы}: значения, которые могут быть использованы только один раз. 89 | \begin{itemize} 90 | \item Меньше сборки мусора. 91 | \item Можно использовать изменяемые структуры данных. 92 | \item Безопасное изменение состояния типа (например, тип файла может включать, открыт ли он). 93 | \end{itemize} 94 | \pause 95 | \item \href{https://serokell.io/blog/2018/12/17/why-dependent-haskell}{Зависимые типы}: типы, параметризованные значениями. 96 | \begin{itemize} 97 | \item Стандартный пример: вектор с размером в типе. 98 | \item Библиотека \hackage{singletons} позволяет эмулировать многие применения зависимых типов. 99 | \end{itemize} 100 | \pause 101 | \item Не совсем будущее: уточнённые типы (refinement types) в Liquid Haskell. 102 | \begin{itemize} 103 | \item \href{https://ruhaskell.org/posts/utils/2016/12/16/liquidhaskell-hello.html}{LiquidHaskell: знакомство} и \href{https://liquid.kosmikus.org/}{Liquid Haskell Tutorial} 104 | \end{itemize} 105 | \end{itemize} 106 | \end{frame} 107 | 108 | \begin{frame}[fragile] 109 | \frametitle{Альтернативы Haskell} 110 | \begin{itemize} 111 | \item OCaml: похож на Haskell без ленивости и (пока) классов типов. 112 | \item Erlang: динамический функциональный язык, ориентированный на многозадачность. 113 | \item Варианты Lisp (можно посмотреть Racket). 114 | \pause 115 | \item Для JVM: Scala, Clojure. 116 | \item Для .Net: F\# (почти тот же OCaml). 117 | \pause 118 | \item ФП без сборщика мусора: Rust. 119 | \begin{itemize} 120 | \item Мощная и интересная система типов, описывающая время жизни значений. 121 | \item Очень быстрое развитие. 122 | \item Реальная альтернатива C++ для низкоуровневого программирования. 123 | \end{itemize} 124 | \pause 125 | \item Языки с зависимыми типами: Agda, Idris, Coq. 126 | \pause 127 | \item Языки спецификаций: TLA+, Alloy. 128 | \end{itemize} 129 | \end{frame} 130 | 131 | \begin{frame}[fragile] 132 | \frametitle{Области математики, связанные с ФП} 133 | \begin{itemize} 134 | \item Лямбда-исчисление: то, с чего всё началось. 135 | \begin{itemize} 136 | \item И множество его обобщений. 137 | \end{itemize} 138 | \pause 139 | \item Математическая логика в целом 140 | \begin{itemize} 141 | \item Логики высших порядков. 142 | \item Изоморфизм Карри-Ховарда. 143 | \item Формализация математики. 144 | \item И автоматический поиск доказательств. 145 | \item Agda, Idris и Coq сюда же! 146 | \end{itemize} 147 | \pause 148 | \item Теория категорий: источник понятий функтора, монады, естественного преобразования. 149 | \end{itemize} 150 | \end{frame} 151 | 152 | 153 | \begin{frame}[fragile] 154 | \frametitle{Дополнительное чтение} 155 | \begin{itemize} 156 | \item \href{http://dev.stephendiehl.com/hask/}{What I Wish I Knew When Learning Haskell} 157 | \item \href{https://lexi-lambda.github.io/blog/2018/02/10/an-opinionated-guide-to-haskell-in-2018/}{An opinionated guide to Haskell in 2018} 158 | \item \href{https://realworldocaml.org/}{Real World OCaml: Functional Programming for the Masses} (книга, доступна бесплатно) 159 | \item \href{https://wiki.portal.chalmers.se/agda/pmwiki.php?n=Main.Documentation}{Много ссылок по Agda} 160 | \item \href{http://docs.idris-lang.org/en/latest/tutorial/index.html}{Введение в Idris} 161 | \item \href{https://doc.rust-lang.org/1.30.0/book/2018-edition/index.html}{The Rust Programming Language} (книга, доступна бесплатно) 162 | \item \href{https://github.com/hmemcpy/milewski-ctfp-pdf}{Category Theory for Programmers} (книга, доступна бесплатно) 163 | \item \href{https://www.hillelwayne.com/post/theorem-prover-showdown/}{The Great Theorem Prover Showdown} 164 | \item \href{https://blog.acolyer.org/2014/11/24/use-of-formal-methods-at-amazon-web-services/}{Use of Formal Methods at Amazon Web Services} 165 | \item \href{https://blog.acolyer.org/2015/01/12/how-to-write-a-21st-century-proof/}{How to write a 21st Century Proof} 166 | \end{itemize} 167 | \end{frame} 168 | 169 | \end{document} 170 | -------------------------------------------------------------------------------- /lecture1_programming_paradigms.eps_tex: -------------------------------------------------------------------------------- 1 | %% Creator: Inkscape inkscape 0.92.2, www.inkscape.org 2 | %% PDF/EPS/PS + LaTeX output extension by Johan Engelen, 2010 3 | %% Accompanies image file 'lecture1_programming_paradigms.eps' (pdf, eps, ps) 4 | %% 5 | %% To include the image in your LaTeX document, write 6 | %% \input{.pdf_tex} 7 | %% instead of 8 | %% \includegraphics{.pdf} 9 | %% To scale the image, write 10 | %% \def\svgwidth{} 11 | %% \input{.pdf_tex} 12 | %% instead of 13 | %% \includegraphics[width=]{.pdf} 14 | %% 15 | %% Images with a different path to the parent latex file can 16 | %% be accessed with the `import' package (which may need to be 17 | %% installed) using 18 | %% \usepackage{import} 19 | %% in the preamble, and then including the image with 20 | %% \import{}{.pdf_tex} 21 | %% Alternatively, one can specify 22 | %% \graphicspath{{/}} 23 | %% 24 | %% For more information, please see info/svg-inkscape on CTAN: 25 | %% http://tug.ctan.org/tex-archive/info/svg-inkscape 26 | %% 27 | \begingroup% 28 | \makeatletter% 29 | \providecommand\color[2][]{% 30 | \errmessage{(Inkscape) Color is used for the text in Inkscape, but the package 'color.sty' is not loaded}% 31 | \renewcommand\color[2][]{}% 32 | }% 33 | \providecommand\transparent[1]{% 34 | \errmessage{(Inkscape) Transparency is used (non-zero) for the text in Inkscape, but the package 'transparent.sty' is not loaded}% 35 | \renewcommand\transparent[1]{}% 36 | }% 37 | \providecommand\rotatebox[2]{#2}% 38 | \ifx\svgwidth\undefined% 39 | \setlength{\unitlength}{2150.99414063bp}% 40 | \ifx\svgscale\undefined% 41 | \relax% 42 | \else% 43 | \setlength{\unitlength}{\unitlength * \real{\svgscale}}% 44 | \fi% 45 | \else% 46 | \setlength{\unitlength}{\svgwidth}% 47 | \fi% 48 | \global\let\svgwidth\undefined% 49 | \global\let\svgscale\undefined% 50 | \makeatother% 51 | \begin{picture}(1,0.86843364)% 52 | \put(0,0){\includegraphics[width=\unitlength]{lecture1_programming_paradigms.eps}}% 53 | \put(0.62354661,0.33691851){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{\textsl{Shared state}}}}% 54 | \put(0.12936015,0.43575579){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Deterministic\\ logic programming}}}% 55 | \put(0.12936015,0.37645341){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Relational & logic\\ programming}}}% 56 | \put(0.12936015,0.31715104){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Constraint (logic)\\ programming}}}% 57 | \put(0.12825011,0.25784867){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Concurrent \\ constraint\\ programming}}}% 58 | \put(0.12918581,0.17895317){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Lazy concurrent\\ constraint\\ programming}}}% 59 | \put(0.12825011,0.14418581){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{\textsl{Logic and }\textsl{\\ }\textsl{constraints}}}}% 60 | \put(0.32703476,0.67296528){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Descriptive\\ declarative\\ programming}}}% 61 | \put(0.32703476,0.59389545){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{First-order\\ functional \\ programming}}}% 62 | \put(0.32703472,0.51482563){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Functional\\ programming}}}% 63 | \put(0.32703472,0.44563952){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Continuation\\ programming}}}% 64 | \put(0.42587202,0.43081395){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Monotonic\\ dataflow\\ programming}}}% 65 | \put(0.42587202,0.39622089){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Declarative\\ concurrent\\ programming}}}% 66 | \put(0.32738344,0.33197667){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Lazy\\ dataflow\\ programming}}}% 67 | \put(0.52488365,0.43558146){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Abstract data type\\ functional\\ programming}}}% 68 | \put(0.52470931,0.26773238){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Nonmonotonic\\ dataflow\\ programming}}}% 69 | \put(0.52470931,0.23313934){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Concurrent logic\\ programming}}}% 70 | \put(0.52470931,0.14877896){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Functional reactive\\ programming}}}% 71 | \put(0.52470931,0.12406971){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Weak synchronous\\ programming}}}% 72 | \put(0.52453498,0.05505796){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Strong synchronous\\ programming}}}% 73 | \put(0.62372095,0.37133722){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Abstract data type\\ imperative\\ programming}}}% 74 | \put(0.62354661,0.29244174){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Multi-agent\\ dataflow\\ programming}}}% 75 | \put(0.62354661,0.25784871){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{\textsl{Dataflow and}\textsl{\\ }\textsl{message passing}}}}% 76 | \put(0.72255821,0.34662791){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Event-loop\\ programming}}}% 77 | \put(0.72255821,0.2774418){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Multi-agent\\ programming}}}% 78 | \put(0.72255821,0.25273252){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Message-passing\\ concurrent\\ programming}}}% 79 | \put(0.72255821,0.18354635){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Active object\\ programming}}}% 80 | \put(0.72255821,0.15883705){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Object-capacity\\ programming}}}% 81 | \put(0.72255821,0.13412775){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{\textsl{Dataflow and }\textsl{\\ }\textsl{Message passing}}}}% 82 | \put(0.9202328,0.11436026){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{\textsl{Shared state}}}}% 83 | \put(0.9202328,0.14895333){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Software\\ transactional\\ memory (STM)}}}% 84 | \put(0.9202328,0.25273249){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Concurrent\\ object-oriented\\ programming}}}% 85 | \put(0.9202328,0.21813944){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Shared-state\\ concurrent\\ programming}}}% 86 | \put(0.9202328,0.35651163){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Sequential\\ object-oriented\\ programming}}}% 87 | \put(0.9202328,0.32191859){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Stateful\\ functional\\ programming}}}% 88 | \put(0.92040714,0.44529085){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Imperative\\ programming}}}% 89 | \put(0.82633741,0.35651163){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Imperative\\ search\\ programming}}}% 90 | \put(0.22542233,0.74709328){\color[rgb]{1,0.4,0}\makebox(0,0)[b]{\smash{Unnamed state \\ (sequencial or concurrent)}}}% 91 | \put(0.77180256,0.74709328){\color[rgb]{1,0.4,0}\makebox(0,0)[b]{\smash{Named \\ state}}}% 92 | \put(0.89742849,0.80259839){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{More Imperative\\ Paradigms}}}% 93 | \put(0.17838723,0.80506934){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{More declarative\\ Paradigms}}}% 94 | \put(0.22819746,0.43575579){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Lazy\\ functional\\ programming}}}% 95 | \put(0.32703476,0.29738362){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{Lazy\\ declarative\\ concurrent\\ programming}}}% 96 | \put(0.32703476,0.25290677){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{\textsl{Functional}}}}% 97 | \put(0.32703476,0.68779086){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{record}}}% 98 | \put(0.32703476,0.60872105){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{procedure}}}% 99 | \put(0.32703472,0.52965122){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{closure}}}% 100 | \put(0.52453498,0.07976726){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{instantaneous\\ computation}}}% 101 | \put(0.52342493,0.17366264){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{synchronisation on \\ partial termination}}}% 102 | \put(0.52470931,0.29244171){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{nondeterministic\\ choice}}}% 103 | \put(0.52488365,0.46029079){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{name (unforgetable\\ constant)}}}% 104 | \put(0.72255821,0.36145349){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{port (channel)}}}% 105 | \put(0.72255821,0.2922674){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{thread}}}% 106 | \put(0.72255821,0.19837198){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{local cell}}}% 107 | \put(0.9202328,0.37133722){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{closure}}}% 108 | \put(0.9202328,0.26755808){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{thread}}}% 109 | \put(0.9202328,0.16377891){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{log}}}% 110 | \put(0.82774812,0.37098084){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{search}}}% 111 | \put(0.92040714,0.46011645){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{cell (state)}}}% 112 | \put(0.32703476,0.46046512){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{continuation}}}% 113 | \put(0.42587206,0.4604651){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{thread}}}% 114 | \put(0.42587206,0.44563952){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{single assignment}}}% 115 | \put(0.22819748,0.46046511){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{by-need\\ synchronization}}}% 116 | \put(0.12936018,0.46046512){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{unification\\ (equality)}}}% 117 | \put(0.12936016,0.39127902){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{search}}}% 118 | \put(0.12936015,0.33197664){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{solver}}}% 119 | \put(0.12936015,0.27267425){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{thread}}}% 120 | \put(0.12825011,0.20348816){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{by-need \\ synchronisation}}}% 121 | \put(0.52454915,0.20852878){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{\textsl{nondetermined State}}}}% 122 | \put(0.03052289,0.77180258){\color[rgb]{0.50196078,0.50196078,0}\rotatebox{90}{\makebox(0,0)[b]{\smash{Markup languages\\ (only Datastructures)}}}}% 123 | \put(0.03052289,0.42309696){\color[rgb]{0.50196078,0.50196078,0}\rotatebox{90}{\makebox(0,0)[b]{\smash{Turing complete \\ Languages}}}}% 124 | \put(0.47529067,0.74709328){\color[rgb]{1,0.4,0}\makebox(0,0)[b]{\smash{Undeterministic \\ state}}}% 125 | \put(0.81516931,0.20843003){\color[rgb]{0,0,0}\makebox(0,0)[b]{\smash{\textsl{Observable }\textsl{\\ }\textsl{Nondeterminism}}}}% 126 | \put(0.08790662,0.88651194){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\begin{minipage}{0.18186061\unitlength}\raggedright \end{minipage}}}% 127 | \put(0.03255774,0.01937945){\color[rgb]{0,0,0}\makebox(0,0)[lb]{\smash{}}}% 128 | \put(0.0351934,0.01937945){\color[rgb]{0,0,0}\makebox(0,0)[lb]{\smash{}}}% 129 | \put(0.00883679,0.01937945){\color[rgb]{0,0,0}\makebox(0,0)[lb]{\smash{Peter Van Roy, "Programming Paradigms for Dummies: What Every Programmer Should Know" (2009)}}}% 130 | \end{picture}% 131 | \endgroup% 132 | -------------------------------------------------------------------------------- /lecture2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexeyr/miet-haskell-course/2d8def951630ff403c8843e7c115e8bb100c3080/lecture2.pdf -------------------------------------------------------------------------------- /lecture2.tex: -------------------------------------------------------------------------------- 1 | \documentclass[10pt]{beamer} 2 | \input{lecture_preamble.tex} 3 | 4 | \title{Лекция 2: основы синтаксиса (продолжение)} 5 | 6 | \begin{document} 7 | \begin{frame}[plain] 8 | \maketitle 9 | \end{frame} 10 | 11 | \begin{frame}[fragile] 12 | \frametitle{Существенные отступы} 13 | \begin{itemize} 14 | \item Вспомним виденное раньше 15 | \begin{haskell} 16 | let {x :: Integer; x = 2} 17 | \end{haskell} 18 | По правилам Haskell можно написать это же без фигурных скобок и точки с запятой: 19 | \begin{haskell} 20 | let x :: Integer 21 | x = 2 22 | \end{haskell} 23 | \item Если два выражения (или других фрагмента кода) относятся к одному уровню одного блока, то они должны иметь одинаковый отступ (начинаться в одном и том же столбце). 24 | \item Фрагмент кода, являющийся частью другого, должен иметь отступ больше той строки, где тот начинается. 25 | \item Но не обязательно больше начала его самого. 26 | \end{itemize} 27 | \end{frame} 28 | \begin{frame}[fragile] 29 | \frametitle{Правило преобразования отступов} 30 | \begin{itemize} 31 | \item Если после ключевых слов \haskinline|where|, \haskinline|let|, \haskinline|do|, \haskinline|of| нет открывающей фигурной скобки \haskinline|{|\footnote{Если она есть, то правило перестаёт действовать до соответствующей ей закрывающей.}, то такая скобка вставляется и начинается новый блок. 32 | \item Его базовый отступ "--- столбец, содержащий следующий непробельный символ. 33 | \item После этого каждая строка, которая начинается: 34 | \begin{itemize} 35 | \item в том же столбце: относится к этому блоку и перед ней ставится \haskinline|;| 36 | \item правее: продолжает предыдущую, перед ней ничего не ставится. 37 | \item левее: блок закончился, перед ней ставится \haskinline|}| (одновременно может закончиться несколько блоков). 38 | \end{itemize} 39 | \end{itemize} 40 | \end{frame} 41 | 42 | \begin{frame}[fragile] 43 | \frametitle{Условные выражения} 44 | \begin{itemize} 45 | \item В любом языке нужна возможность выдать результат в зависимости от какого-то условия. 46 | \item В Haskell это выражения \haskinline|if| и \haskinline|case|. 47 | \item Синтаксис \haskinline|if|: 48 | \begin{haskell} 49 | if условие then выражение1 else выражение2 50 | \end{haskell} 51 | Варианта без \haskinline|else| нет. 52 | \item Многострочно пишется так: 53 | \begin{haskell} 54 | if условие 55 | then выражение1 56 | else выражение2 57 | \end{haskell} 58 | \item Тип \haskinline|условия| обязательно \haskinline|Bool|. 59 | \item Типы \haskinline|выражения1| и \haskinline|выражения2| должны совпадать. 60 | \item Это же и тип всего \haskinline|if ... then ... else ...| 61 | \item Ближе к \haskinline|?:|, чем к \haskinline|if| в C-подобных языках. 62 | \end{itemize} 63 | \end{frame} 64 | 65 | \begin{frame}[fragile] 66 | \frametitle{Сопоставление с образцом} 67 | \begin{itemize} 68 | \item Синтаксис \haskinline|case|: \\ 69 | \begin{haskell} 70 | case выражение of 71 | образец1 -> выражение1 72 | образец2 -> выражение2 73 | \end{haskell} 74 | \item Образец это \enquote{форма} для значений типа, которая может содержать несвязанные переменные. Для конкретного значения он либо подходит (и связывает эти переменные), либо не подходит. 75 | \item Процедура вычисления: 76 | \begin{itemize} 77 | \item Вычислить значение \haskinline|выражения|. 78 | \item Сопоставить его с каждым образцом по очереди. 79 | \item Если первым подошёл \haskinline|образецN|, вычислить \haskinline|выражениеN| и вернуть его значение. 80 | \item Если ни один не подошёл, выкинуть ошибку. 81 | \end{itemize} 82 | \end{itemize} 83 | \end{frame} 84 | 85 | \begin{frame}[fragile] 86 | \frametitle{Образцы для известных нам типов} 87 | \begin{itemize} 88 | \item \haskinline|True| и \haskinline|False| "--- образцы для \haskinline|Bool|. Они подходят, только если значение совпадает с ними. 89 | \item Аналогично \haskinline|LT|, \haskinline|EQ| и \haskinline|GT| для \haskinline|Ordering|, а числовые литералы для числовых типов. 90 | \item Переменная "--- образец для любого типа, который подходит для любого значения. 91 | \begin{itemize} 92 | \item В Haskell все переменные в образцах \enquote{свежие} и перекрывают видимые снаружи переменные с тем же названием. 93 | \end{itemize} 94 | \item \haskinline|_| тоже подходит для любого значения любого типа и означает, что это значение не важно. 95 | \item Образцы похожи на выражения, но ими не являются: это новая синтаксическая категория! 96 | \end{itemize} 97 | \end{frame} 98 | 99 | \begin{frame}[fragile] 100 | \frametitle{Связь \haskinline|case| и \haskinline|if|} 101 | \begin{itemize} 102 | \item Пример: 103 | \begin{haskell} 104 | if условие 105 | then выражение1 106 | else выражение2 107 | \end{haskell} 108 | это ровно то же самое, что 109 | \begin{haskell} 110 | case условие of 111 | True -> выражение1 112 | False -> выражение2 113 | \end{haskell} 114 | или 115 | \begin{haskell} 116 | case условие of 117 | True -> выражение1 118 | _ -> выражение2 119 | \end{haskell} 120 | \end{itemize} 121 | \end{frame} 122 | 123 | \begin{frame}[fragile] 124 | \frametitle{Охраняющие условия} 125 | \begin{itemize} 126 | \item У каждого образца могут быть дополнительные условия (зависящие от его переменных): 127 | \begin{haskell} 128 | образец 129 | | условие1 -> выражение1 130 | | условие2 -> выражение2 131 | \end{haskell} 132 | При удачном сопоставлении они проверяются по порядку. Если все оказались ложны, сопоставление переходит к следующему образцу. 133 | \item Последнее условие часто \haskinline|otherwise| (синоним \haskinline|True|), тогда хотя бы одно условие точно истинно. 134 | \item Чтобы сравнить переменную в образце с внешней, нужно использовать \haskinline|==| в условии: 135 | \begin{haskell} 136 | case foo x of 137 | y | y == x -> ... -- не то же, что x -> ... 138 | _ -> ... 139 | \end{haskell} 140 | \end{itemize} 141 | \end{frame} 142 | 143 | \begin{frame}[fragile] 144 | \frametitle{Многоветвенные \haskinline|if|} 145 | \begin{itemize} 146 | \item Цепочка \haskinline|if ... else if ...|, оформленная по правилам отступа, напоминает лестницу: 147 | \begin{haskell} 148 | if условие1 149 | then результат1 150 | else if условие2 151 | then результат2 152 | else результат3 153 | \end{haskell} 154 | \item С расширением \haskinline|MultiWayIf| можем написать: 155 | \begin{haskell} 156 | if | условие1 -> результат1 157 | | условие2 -> результат2 158 | | otherwise -> результат3 159 | \end{haskell} 160 | \item Это можно сделать и с \haskinline|case| без расширений: \only<1>{как?} \pause 161 | \begin{haskell} 162 | case () of _ | условие1 -> ... 163 | \end{haskell} 164 | \end{itemize} 165 | \end{frame} 166 | 167 | \begin{frame}[fragile] 168 | \frametitle{Определение функций по случаям} 169 | \begin{itemize} 170 | \item Часто тело функции "--- \haskinline|case| по аргументу, например. 171 | \begin{haskell} 172 | not x = case x of 173 | True -> False 174 | False -> True 175 | \end{haskell} 176 | \item Такие функции можно записать несколькими равенствами, по одному на ветвь \haskinline|case|: 177 | \begin{haskell} 178 | not True = False 179 | not False = True 180 | \end{haskell} 181 | \item Это работает и с охранными условиями: 182 | \begin{haskell} 183 | not x | x = False 184 | | otherwise = True 185 | \end{haskell} 186 | \item и для нескольких параметров: 187 | \begin{haskell} 188 | nand True True = False 189 | nand _ _ = True 190 | \end{haskell} 191 | \end{itemize} 192 | \end{frame} 193 | 194 | \begin{frame}[fragile] 195 | \frametitle{Локальные определения: \haskinline|let|} 196 | \begin{itemize} 197 | \item Локальные определения дают две выгоды: 198 | \begin{itemize} 199 | \item Более читаемый код. 200 | \item Избавление от повторяющихся вычислений. 201 | \end{itemize} 202 | \item В Haskell два способа их задать: \haskinline|let| и \haskinline|where|. 203 | \item Синтаксис \haskinline|let|: 204 | \begin{haskell} 205 | let переменная1 = выражение1 206 | функция2 x = выражение2 207 | ... 208 | in выражение3 209 | \end{haskell} 210 | \item Всё \haskinline|let ... in ...| "--- выражение. 211 | \item Первое проявление ленивости: будут вычислены только те переменные, которые понадобятся для вычисления \haskinline|выражения3|. 212 | \end{itemize} 213 | \end{frame} 214 | 215 | \begin{frame}[fragile] 216 | \frametitle{Локальные определения: \haskinline|where|} 217 | \begin{itemize} 218 | \item Синтаксис \haskinline|where|: 219 | \begin{haskell} 220 | функция образец1 | условие1 = выражение3 221 | | условие2 = выражение4 222 | where переменная1 = выражение1 223 | функция2 x = выражение2 224 | \end{haskell} 225 | \item Видимы в условиях и в правых частях (но не для других образцов). 226 | \item Можно применить только к определениям. 227 | \item В том числе к локальным. 228 | \end{itemize} 229 | \end{frame} 230 | 231 | \begin{frame}[fragile] 232 | \frametitle{Модули} 233 | \begin{itemize} 234 | \item Программа на Haskell состоит из модулей. 235 | \item Модуль \haskinline|Модуль| (названия с заглавной буквы) определяется в файле \haskinline|Модуль.hs|: 236 | \begin{itemize} 237 | \item Для названия вида \haskinline|A.B.C| будет файл \ghcinline|A/B/C.hs|. 238 | \end{itemize} 239 | \begin{haskell} 240 | module Модуль(функция1) where 241 | 242 | import ... 243 | 244 | функция1 :: ... 245 | функция1 x = ... 246 | \end{haskell} 247 | \item \haskinline|функция1| экспортирована, она доступна другим модулям и GHCi. Все остальные "--- нет. 248 | \item Можно экспортировать всё, опустив список экспортов (включая скобки). 249 | \end{itemize} 250 | \end{frame} 251 | 252 | \begin{frame}[fragile] 253 | \frametitle{Импорт из другого модуля} 254 | \begin{itemize} 255 | \item У директивы \haskinline|import| есть много вариантов. 256 | \item По адресу \url{https://wiki.haskell.org/Import} есть полный перечень. 257 | \item Нам пока достаточно простейших: 258 | \item \begin{haskell} 259 | import Модуль(функция1, функция2) 260 | \end{haskell} 261 | импортирует конкретные функции. 262 | \item \begin{haskell} 263 | import Модуль 264 | \end{haskell} 265 | импортирует всё, что возможно. 266 | \item Импортированные функции доступны как \haskinline|функция| и как \haskinline|Модуль.функция|. 267 | \end{itemize} 268 | \end{frame} 269 | 270 | \begin{frame}[fragile] 271 | \frametitle{Загрузка модуля} 272 | \begin{itemize} 273 | \item В GHCi можно скомпилировать и загрузить модуль (вместе с зависимостями) командой \haskinline|:load Модуль|. Подробности в документации: 274 | \begin{itemize} 275 | \item \href{http://downloads.haskell.org/~ghc/latest/docs/html/users_guide/ghci.html#ghci-load-scope}{The effect of :load on what is in scope} 276 | \end{itemize} 277 | \item Под Windows можно просто дважды щёлкнуть на файл или нажать Enter в Проводнике. 278 | \item Потом при изменениях файла повторить \haskinline|:load| или сделать \haskinline|:reload|. 279 | \item Многие редакторы позволяют это автоматизировать. 280 | \end{itemize} 281 | \end{frame} 282 | 283 | \begin{frame}[fragile] 284 | \frametitle{Stack} 285 | \begin{itemize} 286 | \item Для сборки лабораторных мы используем \href{https://docs.haskellstack.org/en/stable/#quick-start-guide}{Stack}. 287 | \item Ещё одна распространённая система сборки: \href{https://cabal.readthedocs.io/}{Cabal}. 288 | \item Обе ставятся вместе с Haskell через \ghcinline|ghcup|. 289 | \item Основные команды можно посмотреть в документации, но нам пока достаточно 290 | \begin{itemize} 291 | \item \ghcinline|stack repl| для запуска GHCi с загруженными файлами проекта. 292 | \item \ghcinline|stack test| для запуска тестов. 293 | \end{itemize} 294 | \end{itemize} 295 | \end{frame} 296 | 297 | \end{document} 298 | -------------------------------------------------------------------------------- /lecture3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexeyr/miet-haskell-course/2d8def951630ff403c8843e7c115e8bb100c3080/lecture3.pdf -------------------------------------------------------------------------------- /lecture4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexeyr/miet-haskell-course/2d8def951630ff403c8843e7c115e8bb100c3080/lecture4.pdf -------------------------------------------------------------------------------- /lecture5_hof.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexeyr/miet-haskell-course/2d8def951630ff403c8843e7c115e8bb100c3080/lecture5_hof.pdf -------------------------------------------------------------------------------- /lecture5_hof.tex: -------------------------------------------------------------------------------- 1 | \documentclass[10pt]{beamer} 2 | \input{lecture_preamble.tex} 3 | 4 | \title{Лекция 5: функции как значения} 5 | 6 | \begin{document} 7 | \begin{frame}[plain] 8 | \maketitle 9 | \end{frame} 10 | 11 | \begin{frame}[fragile] 12 | \frametitle{Функции как значения} 13 | \begin{itemize} 14 | \item Как упоминалось в начале курса, одно из оснований ФП состоит в том, что функции могут использоваться как значения. 15 | \item В Haskell можно выразиться сильнее:\pause 16 | \item Функции это и есть просто значения, тип которых имеет форму \haskinline|ТипПараметра -> ТипРезультата| для каких-то \haskinline|ТипПараметра| и \haskinline|ТипРезультата|.\pause 17 | \item Мы уже видели примеры этого в равноправии функций и \only<4>{\textbf{других} }переменных. 18 | \end{itemize} 19 | \end{frame} 20 | 21 | \begin{frame}[fragile] 22 | \frametitle{Функции высших порядков} 23 | \begin{itemize} 24 | \item В частности, функции могут принимать на вход функции. 25 | \item То есть тип параметра сам может быть функциональным типом. 26 | \item Тривиальный пример: 27 | \begin{haskell} 28 | foo :: (Char -> Bool) -> Bool 29 | foo f = f 'a' 30 | 31 | ghci> foo isLetter !\pause! 32 | True 33 | \end{haskell} 34 | \item Скобки вокруг типа параметра здесь необходимы.\pause 35 | \item Функции, параметры которых "--- функции, называются \emph{функциями высших порядков (ФВП)}.\pause 36 | \item Часто ими также считают функции, возвращающие функции, но не в Haskell (скоро увидим почему). 37 | \end{itemize} 38 | \end{frame} 39 | 40 | \begin{frame}[fragile] 41 | \frametitle{Лямбда-выражения} 42 | \begin{itemize} 43 | \item В \haskinline|foo| с прошлого слайда можем также передать свою новую функцию, определив её локально: 44 | \begin{haskell} 45 | foo isCyrillic where 46 | isCyrillic c = let lc = toLower c 47 | in 'а' <= lc && lc <= 'я' 48 | \end{haskell} 49 | \pause 50 | \item Но имя этой функции на самом деле не нужно. 51 | \item Как и вообще функциям, которые создаются только как аргументы для других (или как результаты).\pause 52 | \item Вместо этого зададим её через \emph{лямбда-выражение} 53 | \begin{haskell} 54 | foo (\c -> let lc = toLower c 55 | in 'а' <= lc && lc <= 'я') 56 | \end{haskell} 57 | \pause 58 | \item[] 59 | \item Кстати, почему это определение неверно? 60 | \pause 61 | \item Как ни странно, в Юникоде \haskinline|'ё' > 'я'|. 62 | \item И не все буквы кириллицы используются в русском. 63 | \end{itemize} 64 | \end{frame} 65 | 66 | \begin{frame}[fragile] 67 | \frametitle{Лямбда-выражения} 68 | \begin{itemize} 69 | \item Вообще, два определения 70 | \begin{haskell} 71 | функция образец = результат 72 | 73 | функция = \образец -> результат 74 | \end{haskell} 75 | эквивалентны.\pause 76 | \item Одно исключение: \href{https://wiki.haskell.org/Monomorphism_restriction}{для второго может быть выведен менее общий тип}.\pause 77 | \item Лямбда-выражение для функции с несколькими параметрами пишется 78 | \begin{haskell} 79 | \образец1 ... образецN -> результат 80 | \end{haskell} 81 | \pause 82 | \item Например, 83 | \begin{haskell} 84 | Data.List.sortBy (\x y -> compare y x) list 85 | \end{haskell} 86 | \item Что делает это выражение?\pause 87 | \item Сортирует список по убыванию. 88 | \end{itemize} 89 | \end{frame} 90 | 91 | \begin{frame}[fragile] 92 | \frametitle{Лямбда-выражения и \haskinline|case|} 93 | \begin{itemize} 94 | \item Если в обычном определении функции несколько уравнений, например 95 | \begin{haskell} 96 | not True = False 97 | not False = True 98 | \end{haskell} 99 | то в лямбда-выражении придётся использовать \haskinline|case|:\pause 100 | \begin{haskell} 101 | not = \x -> case x of 102 | True -> False 103 | False -> True 104 | \end{haskell} 105 | \pause 106 | или с расширением \haskinline|LambdaCase| 107 | \begin{haskell} 108 | not = \case 109 | True -> False 110 | False -> True 111 | \end{haskell} 112 | \end{itemize} 113 | \end{frame} 114 | 115 | \begin{frame}[fragile] 116 | \frametitle{Несколько стандартных ФВП} 117 | \begin{itemize} 118 | \item В \haskinline|Prelude| есть три функции второго порядка, которые очень часто встречаются в коде Haskell: 119 | \begin{haskell} 120 | ($) :: (a -> b) -> a -> b 121 | f $ x =!\pause! f x!\pause! 122 | 123 | (.) :: (b -> c) -> (a -> b) -> (a -> c) 124 | g . f =!\pause! \x ->!\pause! g (f x) 125 | 126 | flip :: (a -> b -> c) -> (b -> a -> c) 127 | flip f = !\pause! \y x ->!\pause! f x y 128 | \end{haskell} 129 | \item И ещё две в \haskinline|Data.Function|: 130 | \begin{haskell} 131 | (&) = flip (.) 132 | (&) ::!\pause! (a -> b) -> (b -> c) -> (a -> c) 133 | 134 | on :: (b -> b -> c) -> (a -> b) -> 135 | (a -> a -> c) 136 | \end{haskell} 137 | \end{itemize} 138 | \end{frame} 139 | 140 | \begin{frame}[fragile] 141 | \frametitle{Избавление от скобок с помощью \haskinline|$| и \haskinline|.|} 142 | \begin{itemize} 143 | \item Казалось бы, в чём смысл \haskinline|$|: зачем писать \haskinline|f $ x| вместо \haskinline|f x|?\pause 144 | \item Этот оператор имеет минимальный возможный приоритет, так что \haskinline|f (x + y)| можно записать как \haskinline|f $ x + y|.\pause 145 | \item И он правоассоциативен, так что \haskinline|f (g (h x))| можно записать как \haskinline|f $ g $ h x|.\pause 146 | \item Но более принято \haskinline|f . g . h $ x|.\pause 147 | \item \haskinline|.| тоже правоассоциативен, но уже с максимальным приоритетом.\pause 148 | \item[] 149 | \item Пока, наверное, проще читать и писать код со скобками, но стандартный стиль Haskell предпочитает их избегать. 150 | \item Только не перестарайтесь! 151 | \end{itemize} 152 | \end{frame} 153 | 154 | \begin{frame}[fragile] 155 | \frametitle{Правда о функциях многих переменных} 156 | \begin{itemize} 157 | \item Настала пора раскрыть тайну функций многих переменных в Haskell:\pause 158 | \item Их не существует.\pause 159 | \item \haskinline|->| "--- правоассоциативный оператор, так что \haskinline|Тип1 -> Тип2 -> Тип3| это на самом деле \haskinline|Тип1 -> (Тип2 -> Тип3)|: функция, возвращающая функцию.\pause 160 | \item \haskinline|\x y -> результат| это сокращение для \haskinline|\x -> \y -> результат|, а со скобками \haskinline|\x -> (\y -> результат)|.\pause 161 | \item Применение функций (не \haskinline|$|, а пробел), наоборот, левоассоциативно. То есть \haskinline|f x y| читается как \haskinline|(f x) y|. 162 | \end{itemize} 163 | \end{frame} 164 | 165 | \begin{frame}[fragile] 166 | \frametitle{Каррирование} 167 | \begin{itemize} 168 | \item Обычно в математике для сведения функций нескольких аргументов к функциям одного аргумента используется декартово произведение: $A \times B \to C$, а не $A \to (B \to C)$.\pause 169 | \item В Haskell тоже можно было бы писать 170 | \begin{haskell} 171 | foo :: (Int, Int) -> Int 172 | foo (x, y) = x + y 173 | \end{haskell} 174 | для определения функций и \haskinline|foo (1, 2)| для вызова.\pause 175 | \item Но Haskell здесь следует традиции $\lambda$-исчисления.\pause 176 | \item Эти подходы эквивалентны, так как множества $A \times B \to C$ и $A \to (B \to C)$ всегда изоморфны \pause ($C^{A \cdot B} = C^{B^A}$). 177 | \item В Haskell этот изоморфизм реализуют функции\pause 178 | \begin{haskell} 179 | curry :: ((a, b) -> c) -> a -> b -> c 180 | uncurry :: (a -> b -> c) -> (a, b) -> c 181 | \end{haskell} 182 | \end{itemize} 183 | \end{frame} 184 | 185 | \begin{frame}[fragile] 186 | \frametitle{Частичное применение} 187 | \begin{itemize} 188 | \item Преимущество каррированных функций в том, что естественным образом появляется частичное применение. 189 | \item То есть мы можем применить функцию \enquote{двух аргументов} только к первому и останется функция одного аргумента: 190 | \begin{haskell} 191 | ghci> :t sortBy 192 | sortBy :: (a -> a -> Ordering) -> [a] -> [a] 193 | ghci> :t sortBy (flip compare) !\pause! 194 | sortBy (flip compare) :: Ord a => [a] -> [a] 195 | \end{haskell} 196 | \pause 197 | \item Заметьте, что здесь частичное применение в двух местах: \pause\haskinline|flip| можно теперь рассматривать как функцию трёх аргументов! 198 | \end{itemize} 199 | \end{frame} 200 | 201 | \begin{frame}[fragile] 202 | \frametitle{Сечения операторов} 203 | \begin{itemize} 204 | \item Для применения бинарного оператора только к первому аргументу можно использовать его префиксную форму: 205 | \begin{haskell} 206 | ghci> :t (+) 1 207 | (+) 1 :: Num a => a -> a 208 | \end{haskell} 209 | \pause 210 | \item А ко второму? Можно использовать лямбду 211 | \begin{haskell} 212 | \x -> x / 2 213 | \end{haskell} 214 | или \haskinline|flip| 215 | \begin{haskell} 216 | flip (/) 2 217 | \end{haskell} 218 | \pause 219 | \item Но есть специальный синтаксис \haskinline|(арг оп)| и \haskinline|(оп арг)|: 220 | \begin{haskell} 221 | ghci> (1 `div`) 2 !\pause! 222 | 0 223 | ghci> (/ 2) 4 !\pause! 224 | 2.0 225 | \end{haskell} 226 | \end{itemize} 227 | \end{frame} 228 | 229 | \begin{frame}[fragile] 230 | \frametitle{Сечения кортежей} 231 | \begin{itemize} 232 | \item Расширение \haskinline|TupleSections| позволяет частично применять конструкторы кортежей: 233 | \begin{haskell} 234 | ghci> :set -XTupleSections 235 | ghci> (, "I", , "Love", True) 1 False !\pause! 236 | (1,"I",False,"Love",True) 237 | ghci> :t (, "I", , "Love", True) !\pause! 238 | (, "I", , "Love", True) 239 | :: t1 -> t2 -> (t1, [Char], t2, [Char], Bool) 240 | \end{haskell} 241 | \end{itemize} 242 | \end{frame} 243 | 244 | \begin{frame}[fragile] 245 | \frametitle{$\eta$-эквивалентность (сокращение аргументов)} 246 | \begin{itemize} 247 | \item Рассмотрим два определения 248 | \begin{haskell} 249 | foo' x = foo x 250 | -- или foo' = \x -> foo x 251 | \end{haskell} 252 | \item \haskinline|foo' y == foo y|, какое бы \haskinline|y| (и \haskinline|foo|) мы не взяли.\pause 253 | \item Поэтому мы можем упростить определение: 254 | \begin{haskell} 255 | foo' = foo 256 | \end{haskell} 257 | \pause 258 | \item Это также относится к случаям, когда совпадают часть аргументов в конце: 259 | \begin{haskell} 260 | foo' x y z w = foo (y + x) z w 261 | \end{haskell} 262 | эквивалентно 263 | \begin{haskell} 264 | foo' x y = foo (y + x) 265 | \end{haskell} 266 | \end{itemize} 267 | \end{frame} 268 | 269 | \begin{frame}[fragile] 270 | \frametitle{$\eta$-эквивалентность (сокращение аргументов)} 271 | \begin{itemize} 272 | \item И когда само определение такой формы не имеет, но может быть к ней преобразовано 273 | \begin{haskell} 274 | root4 x = sqrt (sqrt x) 275 | \end{haskell} 276 | можно переписать как 277 | \begin{haskell} 278 | root4 x = (sqrt . sqrt) x 279 | 280 | root4 = sqrt . sqrt 281 | \end{haskell} 282 | \item Ещё пример: 283 | \begin{haskell} 284 | ($) :: (a -> b) -> a -> b 285 | f $ x = f x 286 | 287 | ($) f x = f x !\pause! 288 | ($) f = f !\pause! 289 | ($) f = id f !\pause! 290 | ($) = id 291 | \end{haskell} 292 | \end{itemize} 293 | \end{frame} 294 | 295 | \begin{frame}[fragile] 296 | \frametitle{Бесточечный стиль} 297 | \begin{itemize} 298 | \item Как видим, с помощью композиции и других операций можно дать определение некоторых функций без использования переменных. 299 | \item Это называется \emph{бесточечным стилем} (переменные рассматриваются как точки в пространстве значений).\pause 300 | \item Оказывается, что очень многие выражения в Haskell имеют бесточечный эквивалент. 301 | \item На \href{https://wiki.haskell.org/Pointfree}{Haskell Wiki} можно найти инструменты, позволяющие переводить между стилями. 302 | \item Опять же, не перестарайтесь: 303 | \begin{haskell} 304 | > pl \x y -> compare (f x) (f y) 305 | ((. f) . compare .) 306 | \end{haskell} 307 | \pause 308 | \item В языках семейств Forth и APL бесточечный стиль является основным. 309 | \end{itemize} 310 | \end{frame} 311 | 312 | \begin{frame}[fragile] 313 | \frametitle{Программирование, направляемое типами} 314 | \begin{itemize} 315 | \item При реализации полиморфных функций очень часто их тип подсказывает реализацию. 316 | \item Пример: 317 | \begin{haskell} 318 | foo :: (a -> b, b -> Int) -> (a -> Int) 319 | \end{haskell} 320 | Какую часть реализации можно написать сразу? 321 | \begin{haskell} 322 | foo !\pause!(f, g) = !\pause!\x ->!\pause! ? 323 | \end{haskell} 324 | Типы переменных:\pause 325 | \begin{haskell} 326 | f :: a -> b, g :: b -> Int, !\pause!x :: a, ? :: Int 327 | \end{haskell} 328 | Можно увидеть, что \haskinline|?| может быть \haskinline|g ??|, где \haskinline|?? :: |\pause\haskinline|b = |\pause\haskinline|f x|. 329 | \end{itemize} 330 | \end{frame} 331 | 332 | \begin{frame}[fragile] 333 | \frametitle{Типизированные дыры в коде} 334 | \begin{itemize} 335 | \item Эти рассуждения не обязательно делать вручную. 336 | \item Если в выражении (а не в образце) использовать дыру \haskinline|_| (или \haskinline|_название|), то увидим ожидаемый в этом месте тип. 337 | \item 338 | \begin{haskell} 339 | foo :: (a -> b, b -> Int) -> (a -> Int) 340 | foo (f, g) = \x -> g _ 341 | \end{haskell} 342 | выдаст сообщение 343 | \begin{ghci} 344 | Found hole `_' with type: b 345 | Where: `b' is a rigid type variable bound by 346 | the type signature for foo :: (a -> b, b -> Int) -> (a -> Int) 347 | Relevant bindings include ... 348 | \end{ghci} 349 | \item Дыр может быть несколько. 350 | \end{itemize} 351 | \end{frame} 352 | 353 | \end{document} 354 | -------------------------------------------------------------------------------- /lecture6_list_hof.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexeyr/miet-haskell-course/2d8def951630ff403c8843e7c115e8bb100c3080/lecture6_list_hof.pdf -------------------------------------------------------------------------------- /lecture6_list_hof.tex: -------------------------------------------------------------------------------- 1 | \documentclass[11pt]{beamer} 2 | \input{lecture_preamble.tex} 3 | 4 | \title{Лекция 6: работа со списками} 5 | 6 | \begin{document} 7 | \begin{frame}[plain] 8 | \maketitle 9 | \end{frame} 10 | 11 | \begin{frame}[fragile] 12 | \frametitle{Списки в Haskell} 13 | \begin{itemize} 14 | \item Поговорим о списках подробнее. 15 | \item Это структура данных, которая постоянно встречается в программах. 16 | \item Но по поведению и характеристикам \emph{очень сильно} отличается от списков в Java и C\# (или \haskinline|std::vector| в C++). 17 | \pause 18 | \item Списки в Haskell и множестве других языков, начиная с Lisp "--- односвязные. 19 | \pause 20 | \item То есть вместе с каждым элементом хранится ссылка на остальную часть списка. 21 | \item Вспомним определение: 22 | \begin{haskell} 23 | data [a] = [] | a : [a] 24 | \end{haskell} 25 | \end{itemize} 26 | \end{frame} 27 | 28 | \begin{frame}[fragile] 29 | \frametitle{Некоторые функции первого порядка над\\ списками} 30 | \begin{itemize} 31 | \item \haskinline|(++) :: [a] -> [a] -> [a]| \pause"--- объединение. 32 | \item \haskinline[escapeinside=``]|(!!) :: [a] -> Int -> a| \pause"--- элемент по индексу. 33 | \item \haskinline|null :: a -> Bool| \pause"--- проверка на пустоту (лучше, чем \haskinline|length xs == 0|! Но сопоставление ещё лучше). 34 | \item \haskinline|elem :: Eq a => a -> [a] -> Bool| \pause"--- проверка на вхождение элемента. 35 | \item \haskinline|and, or :: [Bool] -> Bool| \pause"--- все ли элементы \haskinline|True| (или есть ли такой). 36 | \pause 37 | \item У части из них тип на самом деле более общий, но пока нам не нужен. 38 | \pause 39 | \item Все основные в \href{https://hackage.haskell.org/package/base/docs/Data-List.html}{Data.List}. \pause И ещё пакет \hackage{safe}. 40 | \end{itemize} 41 | \end{frame} 42 | 43 | \begin{frame}[fragile] 44 | \frametitle{Некоторые функции второго порядка над\\ списками} 45 | \begin{itemize} 46 | \item \haskinline|map :: (a -> b) -> [a] -> [b]| \pause"--- применяет функцию ко всем элементам. 47 | \item \haskinline|filter :: (a -> Bool) -> [a] -> [a]| \pause"--- выбирает элементы, удовлетворяющие условию. 48 | \item \haskinline|concatMap :: (a -> [b]) -> [a] -> [b]| \pause"--- применяет функцию ко всем элементам и объединяет результаты. 49 | \item \haskinline|all, any :: (a -> Bool) -> [a] -> Bool| \pause"--- все ли элементы удовлетворяют условию (или есть ли такой). 50 | \pause 51 | \item[] 52 | \item Упражнение: реализуем какие-нибудь из них (рекурсивно и друг через друга). 53 | \end{itemize} 54 | \end{frame} 55 | 56 | \begin{frame}[fragile] 57 | \frametitle{Сложность функций над списками} 58 | \begin{itemize} 59 | \item Базовые операции "--- конструирование списков и их разбор через \haskinline|:|. 60 | \pause 61 | \item Поэтому \haskinline|length| (а значит, и \haskinline|length xs == 0|) проходит весь список целиком, а \haskinline|null| нет. 62 | \pause 63 | \item \haskinline[escapeinside=``]|xs !! n| "--- цена пропорциональна \pause\haskinline|n|. 64 | \pause 65 | \item Чем определяется цена \haskinline|xs ++ ys|? \pause Длиной \haskinline|xs|. 66 | \pause 67 | \item Добавление в конец списка \haskinline|xs ++ [y]| получается дорогим. 68 | \item В цикле может сделать линейный алгоритм квадратичным. 69 | \pause 70 | \item Вместо добавления в конец часто лучше добавлять в начало, а потом развернуть. 71 | \pause 72 | \item Всё это осложняется ленивостью, о ней позже. 73 | \end{itemize} 74 | \end{frame} 75 | 76 | \begin{frame}[fragile] 77 | \frametitle{Выделения списков (list comprehensions)} 78 | \begin{itemize} 79 | \item Есть удобный способ записи списков, похожий на выражения для множеств. 80 | \item[] \haskinline![x*x | x <- [1..5], even x] == !\pause \haskinline|[4, 16]|. 81 | \item Как \(\left\{x^2\,\middle|\,x \in \{1, \ldots, 5\},\,even(x)\right\}\). 82 | \item \haskinline!x <- [1..5]! "--- \emph{генератор}, \haskinline|even x| "--- \emph{фильтр}. 83 | \pause 84 | \item Тех и других может быть много. 85 | \item Генераторы могут использовать переменные, введённые в предыдущих. Пример: 86 | \begin{haskell} 87 | [(x, y) | x <- [1..5], y <- [1..x], odd (x - y)] == !\pause! 88 | [(3, 2), (5, 2), (5, 4)] 89 | \end{haskell} 90 | \item Эти выражения преобразуются в комбинации \haskinline|concatMap|, \haskinline|map| и \haskinline|filter|: 91 | \begin{haskell} 92 | [x*x | x <- [1..5], even x] == !\pause! 93 | map (\x -> x*x) (filter even [1..5]) 94 | \end{haskell} 95 | \end{itemize} 96 | \end{frame} 97 | 98 | \begin{frame}[fragile] 99 | \frametitle{Правая свёртка списков и структурная рекурсия} 100 | \begin{itemize} 101 | \item При \emph{структурной рекурсии} рекурсивный вызов делается на подтерме одного из аргументов функции (например, хвосте списка). 102 | \item Большинство наших рекурсивных определений выглядели именно так. 103 | \pause 104 | \item Оказывается, что есть универсальная функция, через которую выражаются такие определения: 105 | \begin{haskell} 106 | foldr :: (a -> b -> b) -> b -> [a] -> b 107 | foldr f z [x1, x2, ..., xn] == 108 | x1 `f` (x2 `f` ... (xn `f` z)...) 109 | \end{haskell} 110 | \item Правая свёртка проходит по структуре списка и заменяет \haskinline{[]} на \haskinline{z} и \haskinline{(:)} на \haskinline{f}. 111 | \item Например, \haskinline|sum| получится, если взять \haskinline{f =!\pause! (+)} и \haskinline{z =!\pause! 0}. 112 | \item Упражнение: реализовать \haskinline|length|. 113 | \end{itemize} 114 | \end{frame} 115 | 116 | \begin{frame}[fragile] 117 | \frametitle{Левая свёртка и хвостовая рекурсия} 118 | \begin{itemize} 119 | \item При \emph{хвостовой рекурсии} рекурсивный вызов делается в самом конце вычисления. Например, 120 | \item \begin{haskellsmall} 121 | sum xs = sum' 0 xs where 122 | sum' acc [] = acc 123 | sum' acc (x:xs) = sum' !\pause!(acc + x) xs 124 | \end{haskellsmall} 125 | \item Универсальная функция для таких определений: 126 | \begin{haskellsmall} 127 | foldl :: (b -> a -> b) -> b -> [a] -> b 128 | foldl _f z [] = z 129 | foldl f z (x:xs) = foldl f !\pause!(f z x) xs 130 | \end{haskellsmall} 131 | \item Она проходит по списку слева и получается 132 | \begin{haskellsmall} 133 | (...((z `f` x1) `f` x2) `f`...) `f` xn 134 | \end{haskellsmall} 135 | \item Хвостовая рекурсия может быть скомпилирована в цикл и в других языках ФП очень важна. В Haskell ленивость всё усложняет. 136 | \end{itemize} 137 | \end{frame} 138 | 139 | \begin{frame}[fragile] 140 | \frametitle{Свёртки других типов данных} 141 | \begin{itemize} 142 | \item Сравним объявление списка с типом \haskinline|foldr|: 143 | \begin{haskellsmall} 144 | data [a] = a : [a] | [] 145 | foldr :: (a -> b -> b) -> b -> [a] -> b 146 | \end{haskellsmall} 147 | \item Видим, что у нас по одному аргументу для каждого конструктора: каждый принимает аргументы типов полей этого конструктора (а если там \haskinline|[a]|, то \haskinline|b|) и возвращает \haskinline|b|. 148 | \item Последний аргумент "--- разбираемый список. 149 | \item Для \haskinline|Maybe|: 150 | \begin{haskellsmall} 151 | data Maybe a = Nothing | Just a 152 | foldMaybe :: ? -> ? -> ? -> b !\pause! 153 | foldMaybe :: b -> (a -> b) -> Maybe a -> b 154 | \end{haskellsmall} 155 | \pause 156 | \item Это \haskinline|Data.Maybe.maybe| с обратным порядком аргументов. 157 | \end{itemize} 158 | \end{frame} 159 | 160 | \begin{frame}[fragile] 161 | \frametitle{Свёртка деревьев} 162 | \begin{itemize} 163 | \item Для деревьев возьмём пример, где данные хранятся в листьях, но не в ветвях: 164 | \begin{haskellsmall} 165 | data Tree a = Leaf a | Node (Tree a) (Tree a) 166 | foldTree :: ? -> ? -> Tree a -> b !\pause! 167 | foldTree :: (a -> b) -> (b -> b -> b) -> Tree a -> b !\pause! 168 | 169 | sumTree = foldTree!\pause! id (+) 170 | \end{haskellsmall} 171 | \end{itemize} 172 | \end{frame} 173 | 174 | \begin{frame}[fragile] 175 | \frametitle{Дополнительное чтение} 176 | \begin{itemize} 177 | \item \href{https://habr.com/ru/post/667058/}{Применение обобщённой свёртки для обработки синтаксических деревьев} 178 | \item \href{http://www.cs.nott.ac.uk/~pszgmh/fold.pdf}{A tutorial on the universality and expressiveness of fold} 179 | \item \href{https://wiki.haskell.org/wikiupload/1/14/TMR-Issue6.pdf}{Getting a Fix from the Right Fold} (реализация \haskinline{dropWhile} через \haskinline{foldr}) 180 | \end{itemize} 181 | \end{frame} 182 | 183 | \end{document} 184 | -------------------------------------------------------------------------------- /lecture7_type_inference.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexeyr/miet-haskell-course/2d8def951630ff403c8843e7c115e8bb100c3080/lecture7_type_inference.pdf -------------------------------------------------------------------------------- /lecture8_laziness.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexeyr/miet-haskell-course/2d8def951630ff403c8843e7c115e8bb100c3080/lecture8_laziness.pdf -------------------------------------------------------------------------------- /lecture8_laziness.tex: -------------------------------------------------------------------------------- 1 | \documentclass[11pt]{beamer} 2 | \input{lecture_preamble.tex} 3 | 4 | \title{Лекция 8: ленивость} 5 | 6 | \begin{document} 7 | \begin{frame}[plain] 8 | \maketitle 9 | \end{frame} 10 | 11 | \begin{frame}[fragile] 12 | \frametitle{Передача по значению} 13 | \begin{itemize} 14 | \item Как вычисляется выражение вида \haskinline|f(g(x, y), h(z))| в привычных языках? 15 | \pause 16 | \item Сначала вычисляются аргументы \haskinline|g(x, y)| и \haskinline|h(z)| (гарантированно в этом порядке или нет), потом их значения передаются в \haskinline|f|. 17 | \item Это называется передачей аргументов по значению. 18 | \item Для вычисления выражения нужно вычислить все подвыражения. 19 | \item Исключения в C-подобных языках? 20 | \pause 21 | \item \haskinline|&&|, \haskinline{||}, \haskinline|? :|. 22 | \pause 23 | \item В них вычисляется первый операнд, а второй (и третий для \haskinline|? :|) "--- только если необходимо. 24 | \end{itemize} 25 | \end{frame} 26 | 27 | \begin{frame}[fragile] 28 | \frametitle{Передача по имени} 29 | \begin{itemize} 30 | \item Как можно сделать по-другому? 31 | \pause 32 | \item Макросы в C "--- каждое использование аргумента заменяется на переданное выражение (а не на его значение). 33 | \item И это рекурсивно: макросы в этом определении тоже будут заменены на своё определение. 34 | \item Это передача по имени (почти). 35 | \pause 36 | \item У настоящей передачи по имени нет таких проблем со скобками, как в макросах C. 37 | \pause 38 | \item Аналогичное поведение переменных в командной строке и скриптах Windows и Linux. 39 | \end{itemize} 40 | \end{frame} 41 | 42 | \begin{frame}[fragile] 43 | \frametitle{Сравнение на примерах 1} 44 | \begin{itemize} 45 | \item Если рассмотреть 46 | \begin{haskell} 47 | x = 2 + 2 48 | y = x + x 49 | \end{haskell} 50 | то при передаче по значению будет вычислено \pause 51 | \begin{haskell} 52 | x = 4 53 | y = 4 + 4 54 | \end{haskell} 55 | а при передаче по имени "--- \pause 56 | \begin{haskell} 57 | x = 2 + 2 58 | y = (2 + 2) + (2 + 2) 59 | \end{haskell} 60 | \end{itemize} 61 | \end{frame} 62 | 63 | \begin{frame}[fragile] 64 | \frametitle{Сравнение на примерах 2} 65 | \begin{itemize} 66 | \item 67 | \begin{haskell} 68 | if1(x, y, z) = if x then y else z 69 | 70 | if1(true, 1, 1 / 0) 71 | \end{haskell} 72 | Что случится при передаче по значению? А по имени? 73 | \pause 74 | \item По значению: ошибка. 75 | \pause 76 | \item По имени: \haskinline|1|. 77 | \end{itemize} 78 | \end{frame} 79 | 80 | \begin{frame}[fragile] 81 | \frametitle{Какой порядок лучше?} 82 | \begin{itemize} 83 | \item Плюсы передачи по имени: 84 | \pause 85 | \begin{itemize} 86 | \item Можно избежать ошибок. 87 | \item Можно избежать лишних вычислений 88 | \pause (представьте, что в предыдущем примере вместо \haskinline|1 / 0| вызов сложной функции). 89 | \end{itemize} 90 | \pause 91 | \item Главный минус: 92 | \pause 93 | \begin{itemize} 94 | \item Можно сделать очень много лишних вычислений. 95 | \pause 96 | \item Как только мы используем какую-то переменную более одного раза 97 | \pause (если переменные в её определении не изменились). 98 | \end{itemize} 99 | \end{itemize} 100 | \end{frame} 101 | 102 | \begin{frame}[fragile] 103 | \frametitle{Передача по необходимости} 104 | \begin{itemize} 105 | \item В Haskell используется передача по необходимости: 106 | \item Как передача по имени, но значение каждой переменной вычисляется только один раз. 107 | \pause 108 | \item Это имеет смысл потому, что все переменные неизменяемы. 109 | \pause 110 | \item При этом значения тоже не обязательно вычислять \enquote{до конца}: мы хотим, чтобы 111 | \begin{haskell} 112 | length [1/0, 1/0, 1/0] 113 | \end{haskell} 114 | возвращало \haskinline|3|. 115 | \pause 116 | \item Ленивость в том или ином виде есть во многих языках (пример: \haskinline|System.Lazy| в .NET). 117 | \item Но в Haskell она встроена очень глубоко. 118 | \end{itemize} 119 | \end{frame} 120 | 121 | \begin{frame}[fragile] 122 | \frametitle{Нормальная форма} 123 | \begin{itemize} 124 | \item Выражение находится в нормальной форме, если там нет ничего, что можно вычислить (редуцировать). 125 | \item Примеры: \pause 126 | \begin{haskell} 127 | 42 128 | (2, "hello") 129 | \x -> (x + 1) 130 | \end{haskell} 131 | \item Примеры выражений не в нормальной форме: \pause 132 | \begin{haskell} 133 | 1 + 2 134 | (\x -> x + 1) 2 135 | "he" ++ "llo" 136 | (1 + 1, 2 + 2) 137 | let x = 1 in x 138 | \end{haskell} 139 | \end{itemize} 140 | \end{frame} 141 | 142 | \begin{frame}[fragile] 143 | \frametitle{Теоремы о нормальных формах в лямбда-исчислении} 144 | \begin{itemize} 145 | \item В лямбда-исчислении можно доказать: 146 | \item Два порядка вычисления одного выражения не могут привести к разным значениям (нормальным формам). 147 | \item Но может случиться, что один из них даёт значение, а другой "--- нет. 148 | \item Если какой-то порядок приводит к значению, то передача по имени и по необходимости тоже к нему приведут. 149 | \end{itemize} 150 | \end{frame} 151 | 152 | \begin{frame}[fragile] 153 | \frametitle{Слабая заголовочная нормальная форма} 154 | \begin{itemize} 155 | \item Выражение находится в слабой заголовочной нормальной форме (WHNF), если это: 156 | \begin{itemize} 157 | \item Лямбда. 158 | \item Литерал. 159 | \item Конструктор данных, возможно с аргументами. 160 | \item Функция от $n$ аргументов c $m 2 + 2 + x 168 | (1/0) : take 4 [1..] 169 | (+) 1 170 | \end{haskell} 171 | \end{itemize} 172 | \end{frame} 173 | 174 | \begin{frame}[fragile] 175 | \frametitle{Отложенные вычисления} 176 | \begin{itemize} 177 | \item В Haskell во время исполнения переменная может указывать не только на значение, а на невычисленное выражение (thunk). 178 | \item Если мы напишем 179 | \begin{haskell} 180 | x = (length [1,3], 1/0) 181 | \end{haskell} 182 | то \haskinline|x| вначале указывает именно на thunk. 183 | \item После этого 184 | \begin{haskell} 185 | case x of 186 | (y, z) -> ... 187 | \end{haskell} 188 | требует вычислить внешний конструктор \haskinline|x| (привести к WHNF), и в памяти будет \pause 189 | \begin{haskell} 190 | x: (*thunk1*, *thunk2*) *thunk1*: length [1,3] 191 | y: *thunk1* *thunk2*: 1/0 192 | z: *thunk2* 193 | \end{haskell} 194 | 195 | \end{itemize} 196 | \end{frame} 197 | 198 | \begin{frame}[fragile] 199 | \frametitle{Отложенные вычисления} 200 | \begin{itemize} 201 | \item Если это 202 | \begin{haskell} 203 | case x of 204 | (y, z) -> print y 205 | \end{haskell} 206 | то \haskinline|y| тоже потребуется вычислить, и получим \pause 207 | \begin{haskell} 208 | x: (2, *thunk2*) *thunk1*: 2 209 | y: 2 *thunk2*: 1/0 210 | z: *thunk2* 211 | \end{haskell} 212 | \end{itemize} 213 | \end{frame} 214 | 215 | \begin{frame}[fragile] 216 | \frametitle{\haskinline|:print|} 217 | \begin{itemize} 218 | \item Мы можем увидеть частично вычисленные выражения в GHCi с помощью команд \href{https://downloads.haskell.org/~ghc/8.6.3/docs/html/users_guide/ghci.html#ghci-cmd-:print}{\haskinline|:print|} и \href{https://downloads.haskell.org/~ghc/8.6.3/docs/html/users_guide/ghci.html#ghci-cmd-:sprint}{\haskinline|:sprint|}: 219 | \begin{haskell} 220 | ghci> x = (length [1,3], 1/0 :: Double) 221 | ghci> :sprint x 222 | x = _ 223 | ghci> case x of (y, z) -> 0 224 | 0 225 | ghci> :sprint x 226 | x = (_,_) 227 | ghci> case x of (y, z) -> y 228 | 2 229 | ghci> :sprint x 230 | x = (2,_) 231 | \end{haskell} 232 | \pause 233 | \item Также посмотрите \href{http://felsin9.de/nnis/ghc-vis/}{ghc-vis} (для старых GHC). 234 | \end{itemize} 235 | \end{frame} 236 | 237 | \begin{frame}[fragile] 238 | \frametitle{Примеры} 239 | \begin{itemize} 240 | \item Ещё примеры: 241 | \pause 242 | \item 243 | \begin{haskell} 244 | square x = x*x 245 | square (square (1+1)) 246 | \end{haskell} 247 | \item \haskinline|length (take 2 (filter even [1..]))| 248 | \pause 249 | \item 250 | \begin{haskell} 251 | (&&) :: Bool -> Bool -> Bool 252 | True && x = x 253 | False && x = False 254 | \end{haskell} 255 | ведёт себя как в C автоматически. 256 | \pause 257 | \item Как определить вариант, который начинает вычисление с правого операнда? 258 | \end{itemize} 259 | \end{frame} 260 | 261 | \begin{frame}[fragile] 262 | \frametitle{Минусы ленивости} 263 | \begin{itemize} 264 | \item Вроде бы можно гарантировать, что ленивые вычисления всегда делают не больше шагов, чем энергичные. 265 | \item Но каждый шаг занимает больше времени. 266 | \item Thunks могут занимать больше памяти, чем результат их вычисления (а могут и меньше). 267 | \item Классический пример: 268 | \begin{haskell} 269 | sum [] acc = acc 270 | sum (x:xs) acc = sum xs (x + acc) 271 | 272 | sum [1..100] 0!$\pause \rightsquigarrow$! sum 1:[2..100] 0!$\pause \rightsquigarrow$! 273 | sum [2..100] (1 + 0)!$\pause \rightsquigarrow$! sum 2:[3..100] (1 + 0)!$\pause \rightsquigarrow$! 274 | sum [3..100] (2 + (1 + 0))!$\pause \rightsquigarrow \ \ldots\ \rightsquigarrow$! 275 | (100 + ... + (1 + 0))!$\pause \rightsquigarrow $! 4950 276 | \end{haskell} 277 | \end{itemize} 278 | \end{frame} 279 | 280 | \begin{frame}[fragile] 281 | \frametitle{Минусы ленивости} 282 | \begin{itemize} 283 | \item Из-за этого \haskinline|foldl| "--- ловушка, практически всегда нужно либо \haskinline|foldr|, либо \haskinline|foldl'| (будет позже). 284 | \pause 285 | \item С другой стороны, заметьте, что в этом примере список \haskinline|[1..100]| никогда не появился целиком в памяти! 286 | \pause 287 | \item Ещё один интересный пример: \haskinline|length (take 2 (filter (< 1) [1..]))|. 288 | \item[] 289 | \pause 290 | \item Что же можно сделать? 291 | \pause 292 | \item Часто компилятор может оптимизировать ленивые вычисления (если знает, что это не изменит результата). 293 | \item Или... 294 | \end{itemize} 295 | \end{frame} 296 | 297 | \begin{frame}[fragile] 298 | \frametitle{Строгие вычисления в Haskell: \haskinline|seq|} 299 | \begin{itemize} 300 | \item Мы можем управлять ленивостью явно. 301 | \item Базовый инструмент для этого: \enquote{волшебная} (её нельзя реализовать на самом Haskell) функция \haskinline|seq :: a -> b -> b|. 302 | \pause 303 | \item \haskinline|seq !$e_1$! !$e_2$!| сначала приводит $e_1$ к WHNF, а после этого возвращает $e_2$. 304 | \pause 305 | \item На практике всегда $e_1$ "--- переменная в $e_2$. 306 | \item Пример использования: 307 | \begin{haskell*}{escapeinside=||} 308 | f $! x = seq x (f x) 309 | \end{haskell*} 310 | \item Это аналог \haskinline|$|, только вычисляющий аргумент до WHNF перед вызовом функции. 311 | \end{itemize} 312 | \end{frame} 313 | 314 | \begin{frame}[fragile] 315 | \frametitle{\haskinline|sum| через \haskinline|seq|} 316 | \begin{itemize} 317 | \item Теперь можем определить \haskinline|sum| так:\pause 318 | \begin{haskell*}{escapeinside=||} 319 | sum [] acc = acc 320 | sum (x:xs) acc = sum xs $! x + acc 321 | \end{haskell*} 322 | \item Или так:\pause 323 | \begin{haskell} 324 | sum [] acc = acc 325 | sum (x:xs) acc = let a1 = x + acc in 326 | a1 `seq` sum xs a1 327 | \end{haskell} 328 | \item И процесс вычисления: 329 | \begin{haskell} 330 | sum [1..100] 0 !$\rightsquigarrow$! sum 1:[2..100] 0 !$\pause \rightsquigarrow$! 331 | let a1 = 1 + 0 in a1 `seq` sum [2..100] a1 !$\pause \rightsquigarrow$! 332 | sum [2..100] 1 !$\rightsquigarrow$! sum 2:[3..100] 1 !$\pause \rightsquigarrow$! 333 | let a1 = 2 + 1 in a1 `seq` sum [3..100] a1 !$\pause \rightsquigarrow$! 334 | !$\rightsquigarrow\ \ldots\ \rightsquigarrow\ $!4950 335 | \end{haskell} 336 | \end{itemize} 337 | \end{frame} 338 | 339 | \begin{frame}[fragile] 340 | \frametitle{\haskinline|foldl'|} 341 | \begin{itemize} 342 | \item Мы знаем, что сумма списка "--- частный случай свёртки. 343 | \item Для функций \haskinline|product|, \haskinline|minimum|, \haskinline|maximum| имеем ровно те же проблемы! 344 | \item Можем определить вариант \haskinline|foldl| (более общая версия в \haskinline|Data.Foldable|): 345 | \begin{haskell} 346 | foldl' :: (a -> b -> a) -> a -> [b] -> a 347 | foldl' f a [] = a 348 | foldl' f a (x:xs) = let a' = f a x 349 | in a' `seq` foldl' f a' xs 350 | \end{haskell} 351 | \item \pause 352 | \begin{haskell} 353 | sum xs = foldl' (+) 0 xs 354 | product xs = foldl' (*) 1 xs 355 | minimum xs = foldl1' min xs 356 | ... 357 | \end{haskell} 358 | \end{itemize} 359 | \end{frame} 360 | 361 | \begin{frame}[fragile] 362 | \frametitle{Строгие переменные в образцах} 363 | \begin{itemize} 364 | \item В \haskinline|foldl'| параметр \haskinline|acc| вычисляется только до WHNF. Если мы вычисляем среднее так 365 | \begin{haskell} 366 | mean :: [Double] -> Double 367 | mean xs = s' / fromIntegral l' 368 | where (s', l') = foldl' step (0, 0) xs 369 | step (s, l) a = (s + a, l + 1) 370 | \end{haskell} 371 | то в \haskinline|s| и \haskinline|l| снова накапливаются вычисления! 372 | \pause 373 | \item Включив \haskinline|BangPatterns|, сделаем их строгими: 374 | \begin{haskell*}{escapeinside=||} 375 | step (!s, !l) a = (s + a, l + 1) 376 | \end{haskell*} 377 | \item Это превращается в 378 | \begin{haskell} 379 | step (s, l) a = let s' = s + a; l' = l + 1 380 | in s' `seq` l' `seq` (s', l') 381 | \end{haskell} 382 | \item Правила перевода \href{https://downloads.haskell.org/~ghc/8.6.3/docs/html/users_guide/glasgow_exts.html#recursive-and-polymorphic-let-bindings}{довольно сложные}. 383 | \end{itemize} 384 | \end{frame} 385 | 386 | \begin{frame}[fragile] 387 | \frametitle{Строгие поля в типах данных} 388 | \begin{itemize} 389 | \item Другой способ решить ту же проблему: 390 | \begin{haskellsmall*}{escapeinside=||} 391 | data SPair a b = SPair !a !b 392 | 393 | mean :: [Double] -> Double 394 | mean xs = s' / fromIntegral l' 395 | where SPair s' l' = foldl' step (0, 0) xs 396 | step (SPair s l) a = SPair (s + a) (l + 1) 397 | \end{haskellsmall*} 398 | \pause 399 | \item Восклицательный знак означает, что любое вычисление значения с этим конструктором вычислит и эти поля (до WHNF). 400 | \item То есть конструктор работает как 401 | \begin{haskell} 402 | SPair a b = a `seq` b `seq` SPair' a b 403 | \end{haskell} 404 | где \haskinline|SPair'| "--- конструктор, который получится без строгих полей. 405 | \end{itemize} 406 | \end{frame} 407 | 408 | \begin{frame}[fragile] 409 | \frametitle{Дополнительное чтение} 410 | \begin{itemize} 411 | \item \href{https://blog.acolyer.org/2016/09/14/why-functional-programming-matters/}{Why Functional Programming Matters} (статья, написанная до появления собственно Haskell) 412 | \item \href{https://www.fpcomplete.com/blog/2017/09/all-about-strictness}{All About Strictness} 413 | \item \href{https://apfelmus.nfshost.com/articles/lazy-eval.html}{The Incomplete Guide to Lazy Evaluation} 414 | \item \href{https://en.wikibooks.org/wiki/Haskell/Laziness}{Laziness в Haskell Wikibook} 415 | \end{itemize} 416 | \end{frame} 417 | 418 | \end{document} 419 | -------------------------------------------------------------------------------- /lecture9_higher_kinded_types.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexeyr/miet-haskell-course/2d8def951630ff403c8843e7c115e8bb100c3080/lecture9_higher_kinded_types.pdf -------------------------------------------------------------------------------- /lecture_preamble.tex: -------------------------------------------------------------------------------- 1 | \usepackage[utf8]{inputenc} 2 | \usepackage[T1,T2A]{fontenc} 3 | \usepackage[russian]{babel} 4 | \usepackage{color} 5 | \usepackage{calc} 6 | \usepackage{graphicx} 7 | \usepackage{epstopdf} 8 | \usepackage{hyperref} 9 | \hypersetup{unicode,colorlinks} 10 | \usetheme[progressbar=head,numbering=fraction,block=fill]{metropolis} 11 | \usepackage{minted} 12 | \usepackage{dejavu} 13 | %\usepackage{adjustbox} % Позволяет сузить куски кода (или текст) ровно настолько, чтобы уместиться в слайд 14 | \usepackage{csquotes} 15 | \usepackage{upquote} 16 | 17 | \usemintedstyle{solarized-light} 18 | \newminted[haskell]{haskell}{ 19 | escapeinside=!!, 20 | mathescape=true, 21 | texcomments=true, 22 | beameroverlays=true, 23 | autogobble=true, 24 | fontsize=\small, 25 | breaklines=false % Лучше сам поставлю переносы на удобных местах 26 | } 27 | \newminted[haskellsmall]{haskell}{ 28 | escapeinside=!!, 29 | mathescape=true, 30 | texcomments=true, 31 | beameroverlays=true, 32 | autogobble=true, 33 | fontsize=\footnotesize, 34 | breaklines=false 35 | } 36 | \newminted[haskelltiny]{haskell}{ 37 | escapeinside=!!, 38 | mathescape=true, 39 | texcomments=true, 40 | beameroverlays=true, 41 | autogobble=true, 42 | fontsize=\scriptsize, 43 | breaklines=false 44 | } 45 | \newmintinline[haskinline]{haskell}{ 46 | escapeinside=!!, 47 | mathescape=true, 48 | beameroverlays=true, 49 | breaklines=true 50 | } 51 | \newminted[ghci]{text}{ 52 | autogobble=true, 53 | fontsize=\small, 54 | breaklines=false 55 | } 56 | \newminted[ghcismall]{text}{ 57 | autogobble=true, 58 | fontsize=\footnotesize, 59 | breaklines=false 60 | } 61 | \newminted[ghcitiny]{text}{ 62 | autogobble=true, 63 | fontsize=\scriptsize, 64 | breaklines=false 65 | } 66 | \newmintinline[ghcinline]{text}{ 67 | breaklines=true 68 | } 69 | 70 | \newcommand{\hackage}[1]{\href{https://hackage.haskell.org/package/#1}{#1}} 71 | 72 | \vfuzz=20pt % позволяет тексту дойти до номера слайда 73 | 74 | \author{Алексей Романов} 75 | \subtitle{Функциональное программирование на Haskell} 76 | %\logo{} 77 | \institute{МИЭТ} 78 | \subject{Функциональное программирование на Haskell} 79 | %\setbeamercovered{transparent} 80 | %\setbeamertemplate{navigation symbols}{} 81 | -------------------------------------------------------------------------------- /my_style.py: -------------------------------------------------------------------------------- 1 | from pygments.styles.solarized import SolarizedLightStyle 2 | from pygments.token import Keyword 3 | 4 | class MyStyle(SolarizedLightStyle): 5 | styles = SolarizedLightStyle.styles 6 | styles[Keyword.Declaration] = styles[Token] 7 | -------------------------------------------------------------------------------- /paradigmsDIAGRAMeng108.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexeyr/miet-haskell-course/2d8def951630ff403c8843e7c115e8bb100c3080/paradigmsDIAGRAMeng108.pdf --------------------------------------------------------------------------------