diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1383faf --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Dr. Kat + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..9e1ea72 --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +site: + stack build + stack exec -- homepage rebuild + +upload: + cp -r _site/* docs/ + cd docs/ && git add . && git commit -a -m "update page" && git push origin main + +clean: + rm -rf *.hi *.o .*.swp .*.swo website _site/ _cache/ + +server: + stack exec -- homepage watch diff --git a/README.md b/README.md new file mode 100644 index 0000000..5a16167 --- /dev/null +++ b/README.md @@ -0,0 +1,130 @@ +# 130-web + + +Public course materials for [UCSD CSE 130: Winter 2024](https://ucsd-cse130.github.io/wi24/) + +## Install + +You too, can build this webpage locally, like so: + +```bash +git clone git@github.com:ucsd-cse130/wi24.git +cd wi24 +make +``` + +To then update the webpage after editing stuff, do: + +```bash +make upload +``` + +The website will live in `_site/`. + +## Customize + +By editing the parameters in `siteCtx` in `Site.hs` + +## View + +You can view it by running + +```bash +make server +``` + +## Update + +Either do + +```bash +make upload +``` + +or, if you prefer + +```bash +make +cp -r _site/* docs/ +git commit -a -m "update webpage" +git push origin main +``` + +## To build Lecture Versions + +To build the "lecture" version of all the html i.e. *without* +the answers to quizzes and other questions, replace the +following in `Site.hs` + +```haskell + crunchWithCtxCustom "final" postCtx +``` + +with + +```haskell + crunchWithCtxCustom "lecture" postCtx +``` + +Then, as you go through the lectures, replace `match "lectures/*"` with + +``` +match "lectures/00-*" $ crunchWithCtxCustom "final" postCtx +match "lectures/*" $ crunchWithCtxCustom "lecture" postCtx +``` + +(and gradually add more and more lectures to `final` as I go through them) + +## New Class Checklist + +- [x] site.hs +- [x] index.md +- [x] links.md +- [x] contact.md +- [x] lectures.md +- [x] calendar.md +- [x] grades.md +- [x] assignments.md +- [x] piazza + +- [ ] canvas +- [ ] github registration form +- [ ] clicker registration form +- [ ] 00-lambda + +* [-] groups +* [-] seating chart + +## Credits + +This theme is a fork of [CleanMagicMedium-Jekyll](https://github.com/SpaceG/CleanMagicMedium-Jekyll) +originally published by Lucas Gatsas. + + diff --git a/assignments.md b/assignments.md new file mode 100644 index 0000000..5274aa3 --- /dev/null +++ b/assignments.md @@ -0,0 +1,49 @@ +--- +title: Assignments +headerImg: angles.jpg +--- + +## Late Policy + +You have a total of *six late days* that you can use throughout the quarter, +but no more than *four late days* per assignment. + +- A late day means anything between 1 second and 23 + hours 59 minutes and 59 seconds past a deadline +- If you submit past the late day limit, you get 0 points for that assignment +- There is no penalty for submitting late but within the limit + +## Assignments + +| **HW-link** | **Name** | **Deadline (23:59:59pm)** | +|:-------------------------------------------------|:------------------------|:---------------------------| +| [HW #0] | Lambda Calculus | Wed 1/24 | + + + +## Sample Exam Questions + +Note: 2018 exams are the most representative. Older exams use OCaml instead of Haskell. +Check out these [lecture notes](https://ucsd-cse130.github.io/web/lectures/02-haskell.html) +for a comparison between the two languages. + +- [Midterm Sp 18](/static/raw/130-midterm-sp18.pdf) ([solution](/static/raw/130-midterm-sp18-solution.pdf)), + +- [Midterm Sp 15](/static/raw/midterm-sp15.pdf), ocaml code ported to [haskell](/static/raw/MidtermSp15.hs) + +- [Midterm Sp 14](/static/raw/midterm-sp14.pdf), ocaml code ported to [haskell](/static/raw/MidtermSp14.hs) + +- [Midterm Sp 12](/static/raw/midterm-sp12.pdf), [Midterm Wi 12](/static/raw/midterm-wi12.pdf). + +- [Final Sp 18](/static/raw/130-final-sp18.pdf) ([solution](/static/raw/130-final-sp18-solution.pdf)), + [Final Sp 14](/static/raw/final-sp14.pdf), + [Final Fa 12](/static/raw/final-fa12.pdf), + [Final Sp 12](/static/raw/final-sp12.pdf), + [Final Fa 11](/static/raw/final-fa11.pdf), + [Final Wi 11](/static/raw/final-wi11.pdf). diff --git a/calendar.md b/calendar.md new file mode 100644 index 0000000..7745384 --- /dev/null +++ b/calendar.md @@ -0,0 +1,18 @@ +--- +title: Calendar +headerImg: road.jpg +--- + +## Office Hours + +See CANVAS calendar for details + +## Calendar + +| **Event** | **Day** | **Time** | +|:-------------|:---------|:-------------| +| *Lectures* | Tu-Th | 12:30-1:50pm | +| *Section* | Fri | 3:00-3:50pm | +| *Midterm I* | Tu 1/30 | 12:30-1:50pm | +| *Midterm II* | Th 2/22 | 12:30-1:50pm | +| *Final* | Tu 3/19 | 11:30-2:30pm | diff --git a/contact.md b/contact.md new file mode 100644 index 0000000..2b77bb5 --- /dev/null +++ b/contact.md @@ -0,0 +1,26 @@ +--- +title: Contact +headerImg: world-map.jpg +--- + +## Course Details + +- **Lectures:** Tu-Th 12:30-1:50p at Pepper Canyon 106 +- **Section:** Fr 5:00-3:50p at Pepper Canyon 109 +- **Announcements:** Posted on Piazza. Please check often! + +## Instructor + +- [Ranjit Jhala](https://ranjitjhala.github.io) + +## Teaching Assistants + +- [Matt Kolosick](mailto:mkolosick@ucsd.edu) +- [Cole Kurashige](mailto:ckurashige@ucsd.edu) +- [George Sakkas](mailto:gsakkas@ucsd.edu) + +## Tutors + +- [Rana Lulla](mailto:rlulla@ucsd.edu) +- [Matthew Peng](mailto:mapeng@ucsd.edu) +- [Melody Ruth](mailto:maruth@ucsd.edu) \ No newline at end of file diff --git a/discussions/final-review.md b/discussions/final-review.md new file mode 100644 index 0000000..90c1970 --- /dev/null +++ b/discussions/final-review.md @@ -0,0 +1,106 @@ +--- +title: Final Exam Review +headerImg: sea.jpg +date: 2020-03-17 +--- +## Tips for Writing Programs about Programming Languages + +### Start with the base cases + +They are usually more straightforward and don't require thinking about recursion. + +### Pay attention to the types you have and what you're trying to produce + +My process for writing `eval env (EVar x)` looks something like: + +1. What is the type of `eval`? It's `Env -> Expr -> Value` where `Env = [(Id, Value)]` +2. We're working with `EVar x` so we also have `x` of type `Id`. +3. So at hand we have: an `Id`, an `Env` that is a `[(Id, Value)]`, and if we look at our helper functions we have a function `lookupId :: [(Id, Value)] -> Id -> Value`. +4. This looks like a good solution so let's start there and we see that `lookupId env x` works and does what it should do. + +We can follow this for other base cases where it's sometimes even more straightforward. +For `eval env (EInt i)` we have `i` of type `Int`. +We also have `VInt` which takes an `Int` and produces a `Value`. +And indeed the implementation is `eval env (EInt i) = VInt i`. + +### Contexts/Environments + +`env` was very important to the previous example and these contexts/environments show up frequently in programming languages. +Whenever we are dealing with a variable, we probably want to do it using the environment. +We will see an example of this next when talking about `EApp`. + +### Recursion + +For cases with sub `Expr`essions: these will probably be recursive. +So anytime we see a case like `eval env (EApp e1 e2)` we're probably going to want to call `eval ___ e1` and `eval ___ e2`. +Keep these around as `eval` let's us produce `Value`s which is our eventual goal. + +My first step for thinking about these "complex" expressions is to think of a specific example (doing the base cases first will help here), say `(\x -> x) 1` which we know should evaluate to `1`. +This case would look like +``` +eval env (EApp (ELam "x" (EVar "x")) (EInt 1)) +``` +However, we are just using this to help us figure out how to write `eval env (EApp e1 e2)` so keep `e1 = (ELam "x" (EVar "x"))` and `e2 = (EInt 1)` in mind but don't focus too much on them. + +At this point we have hopefully written the `eval` cases for `ELam` and `EInt`. +So we know that `eval env e1` in this case will return `VClos env "x" (EVar "x")`. +We also know that `eval env e2` will return `VInt 1`. + +Now we see that we're dealing with a variable particularly the `"x"` argument of the closure. +A closure is a function values so it is waiting for an argument to substitute for `"x"`. +What does substitution mean though? +Here substitution means that, when we evaluate the body (`EVar x`), we want it to return `1`. +When does `eval ___ (EVar x)` return `1`? +Why when `___` has the binding `(x, VInt 1)`. + +Now we have arrived at the partial implementation: +``` +eval env (EApp e1 e2) = eval (x, v2):___ body + where + VClos closEnv x body = eval env e1 + v2 = eval env e2 +``` + +Lastly we have to figure out what to put as the rest of the environment when evaluating `body`. +Here it is time to once again take stock of what types of things we have, in particular, what things of type `Env` we have (it's `env` and `closEnv`). +Now it pays to think about the case of `eval env (ELam x body) = VClos env x body`. +Remember that the `env` when evaluating `(ELam x body)` is the `closEnv` in the `EApp` case. +Here it pays to try to think about why we have `closEnv`. +It captures the environment when a function is defined so let's think about where that might be the case: +``` +let x = 1 in +let f = \y -> x in +let x = 2 in +f 1 +``` +This should evaluate to `1`. Importantly for our implementation of `eval` for `EApp` it is the `closEnv` when defining the lambda that contains `(x, VInt 1)` whereas `env` at the application site contains `(x, VInt 2)`. +So we arrive at +``` +eval env (EApp e1 e2) = eval (x, v2):closEnv body + where + VClos closEnv x body = eval env e1 + v2 = eval env e2 +``` + +### Don't be more specific than you need to be + +While we used `EInt 1` as the argument in our example we never had to look inside of it, all we had to do was call `eval env e2`. +You should try to do this whenever possible: if you don't need to know specific information about a subterm besides what it evaluates to (or whatever other thing you are computing) then just do the recursive call on it. + +For non-recursive `ELet` we don't actually care what `e1` or `e2` in `let x = e1 in e2` are. +All we need to do is evaluate `e1` to `v1`, see that we have a variable, add `(x, v1)` to the environment, and then evaluate `e2` in the new environment. +In that description it never mattered what the actual expressions `e1` and `e2` are so we shouldn't pay attention to that. +Just treat them as abstract expressions. + +This is in contrast to `e1` in the `EApp` case because there we need to know that the first thing evaluates to a function. +But notice that we only care that it *evaluates* to a function, so we only need to look at `e1` after evaluating it. +This is a good general rule for writing recursive functions for programming languages: poke the subterms with the function you're defining (here call `eval env e1`), *then* look at what you get back. + +## WRITE AND RUN TESTS + +When writing a case have a test in mind and run your code on it. +When we were writing the `eval` case for `EApp` above we had the test case `(\x -> x) 1` in mind. +After you write the code hop over to GHCI and run that test case and see if it does what you expect. +Don't wait until you've implemented every case and then test some large chunk of code because debugging that will be hard. + +## Good luck. diff --git a/docs/assignments.html b/docs/assignments.html new file mode 100644 index 0000000..bbc8eea --- /dev/null +++ b/docs/assignments.html @@ -0,0 +1,371 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

Assignments

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

Late Policy

+

You have a total of five late days that you can use throughout the quarter, but no more than four late days per assignment.

+
    +
  • A late day means anything between 1 second and 23 hours 59 minutes and 59 seconds past a deadline
    +
  • +
  • If you submit past the late day limit, you get 0 points for that assignment
  • +
  • There is no penalty for submitting late but within the limit
  • +
+

Assignments

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HW-linkNameDeadline (23:59:59pm)
HW #0Lambda CalculusWed 1/19
HW #1Introduction to HaskellFri 1/28
HW #2Data typesFri 2/11
HW #3Higher-order FunctionsFri 2/25
HW #4InterpretersMon 3/7
HW #5ClassesSun 3/20 (no late days)
+

Sample Exam Questions

+

Note: 2018 exams are the most representative. Older exams use OCaml instead of Haskell. Check out these lecture notes for a comparison between the two languages.

+ +
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/calendar.html b/docs/calendar.html new file mode 100644 index 0000000..dd71b40 --- /dev/null +++ b/docs/calendar.html @@ -0,0 +1,346 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

Calendar

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

Office Hours

+

See CANVAS calendar for details

+

Calendar

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
EventDayTime
LecturesTu-Th09:30-10:50am
SectionMon08:00-08:50am
MidtermThu 2/1009:30-10:50am
FinalTue 3/1508:00-11:00am
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/contact.html b/docs/contact.html new file mode 100644 index 0000000..65529e6 --- /dev/null +++ b/docs/contact.html @@ -0,0 +1,334 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

Contact

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

Course Details

+
    +
  • Lectures: Tu-Th 09:30-10:50am at P416 WEST (+ Zoom)
  • +
  • Section: W 08:00-08:50am at P416 WEST (+ Zoom)
  • +
  • Announcements: Posted on Piazza. Please check often!
  • +
+

Instructor

+ +

Teaching Assistants

+ +

Tutors

+ +
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/discussions/final-review.html b/docs/discussions/final-review.html new file mode 100644 index 0000000..ecf3b5f --- /dev/null +++ b/docs/discussions/final-review.html @@ -0,0 +1,355 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

Final Exam Review

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

Tips for Writing Programs about Programming Languages

+

Start with the base cases

+

They are usually more straightforward and don’t require thinking about recursion.

+

Pay attention to the types you have and what you’re trying to produce

+

My process for writing eval env (EVar x) looks something like:

+
    +
  1. What is the type of eval? It’s Env -> Expr -> Value where Env = [(Id, Value)]
  2. +
  3. We’re working with EVar x so we also have x of type Id.
  4. +
  5. So at hand we have: an Id, an Env that is a [(Id, Value)], and if we look at our helper functions we have a function lookupId :: [(Id, Value)] -> Id -> Value.
  6. +
  7. This looks like a good solution so let’s start there and we see that lookupId env x works and does what it should do.
  8. +
+

We can follow this for other base cases where it’s sometimes even more straightforward. For eval env (EInt i) we have i of type Int. We also have VInt which takes an Int and produces a Value. And indeed the implementation is eval env (EInt i) = VInt i.

+

Contexts/Environments

+

env was very important to the previous example and these contexts/environments show up frequently in programming languages. Whenever we are dealing with a variable, we probably want to do it using the environment. We will see an example of this next when talking about EApp.

+

Recursion

+

For cases with sub Expressions: these will probably be recursive. So anytime we see a case like eval env (EApp e1 e2) we’re probably going to want to call eval ___ e1 and eval ___ e2. Keep these around as eval let’s us produce Values which is our eventual goal.

+

My first step for thinking about these “complex” expressions is to think of a specific example (doing the base cases first will help here), say (\x -> x) 1 which we know should evaluate to 1. This case would look like

+
eval env (EApp (ELam "x" (EVar "x")) (EInt 1)) 
+

However, we are just using this to help us figure out how to write eval env (EApp e1 e2) so keep e1 = (ELam "x" (EVar "x")) and e2 = (EInt 1) in mind but don’t focus too much on them.

+

At this point we have hopefully written the eval cases for ELam and EInt. So we know that eval env e1 in this case will return VClos env "x" (EVar "x"). We also know that eval env e2 will return VInt 1.

+

Now we see that we’re dealing with a variable particularly the "x" argument of the closure. A closure is a function values so it is waiting for an argument to substitute for "x". What does substitution mean though? Here substitution means that, when we evaluate the body (EVar x), we want it to return 1. When does eval ___ (EVar x) return 1? Why when ___ has the binding (x, VInt 1).

+

Now we have arrived at the partial implementation:

+
eval env (EApp e1 e2) = eval (x, v2):___ body
+  where
+    VClos closEnv x body = eval env e1
+    v2 = eval env e2
+

Lastly we have to figure out what to put as the rest of the environment when evaluating body. Here it is time to once again take stock of what types of things we have, in particular, what things of type Env we have (it’s env and closEnv). Now it pays to think about the case of eval env (ELam x body) = VClos env x body. Remember that the env when evaluating (ELam x body) is the closEnv in the EApp case. Here it pays to try to think about why we have closEnv. It captures the environment when a function is defined so let’s think about where that might be the case:

+
let x = 1 in
+let f = \y -> x in
+let x = 2 in
+f 1
+

This should evaluate to 1. Importantly for our implementation of eval for EApp it is the closEnv when defining the lambda that contains (x, VInt 1) whereas env at the application site contains (x, VInt 2). So we arrive at

+
eval env (EApp e1 e2) = eval (x, v2):closEnv body
+  where
+    VClos closEnv x body = eval env e1
+    v2 = eval env e2
+

Don’t be more specific than you need to be

+

While we used EInt 1 as the argument in our example we never had to look inside of it, all we had to do was call eval env e2. You should try to do this whenever possible: if you don’t need to know specific information about a subterm besides what it evaluates to (or whatever other thing you are computing) then just do the recursive call on it.

+

For non-recursive ELet we don’t actually care what e1 or e2 in let x = e1 in e2 are. All we need to do is evaluate e1 to v1, see that we have a variable, add (x, v1) to the environment, and then evaluate e2 in the new environment. In that description it never mattered what the actual expressions e1 and e2 are so we shouldn’t pay attention to that. Just treat them as abstract expressions.

+

This is in contrast to e1 in the EApp case because there we need to know that the first thing evaluates to a function. But notice that we only care that it evaluates to a function, so we only need to look at e1 after evaluating it. This is a good general rule for writing recursive functions for programming languages: poke the subterms with the function you’re defining (here call eval env e1), then look at what you get back.

+

WRITE AND RUN TESTS

+

When writing a case have a test in mind and run your code on it. When we were writing the eval case for EApp above we had the test case (\x -> x) 1 in mind. After you write the code hop over to GHCI and run that test case and see if it does what you expect. Don’t wait until you’ve implemented every case and then test some large chunk of code because debugging that will be hard.

+

Good luck.

+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/grades.html b/docs/grades.html new file mode 100644 index 0000000..be79a81 --- /dev/null +++ b/docs/grades.html @@ -0,0 +1,328 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

Grading

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

Homework Assignments (45%)

+

There will be six programming assignments, assigned every one or two weeks. Instructions on turning them in will be posted with each assignment.

+

Partners

+
    +
  • You can work on the assignment either alone or in groups of two.
  • +
  • Every student has to turn in their solution, even if their solution is identical to their partner’s.
  • +
+

Midterm (25%)

+

Will be held in class on Thursday February 10.

+

The format will be announced before the midterm.

+

Final (30%)

+

Will be held on Tuesday March 15.

+

The format will be announced before the final.

+

Since the final is cumulative, your midterm grade will be calculated as midterm > 0 ? max(final, midterm) : 0. This means that you get a second chance if you don’t do well on the midterm but you must turn in both the midterm and the final.

+

Discussions (+5%)

+

Extra credit for top-20 best participants in Piazza discussions, determined by the instruction team.

+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..26e7c11 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,323 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

Programming Languages

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

Overview

+

Programming Languages are the duct tape, bricks, mortar and steel of the information age. Over the last thirty years, a variety of languages with diverse features have been developed, expressing radically different perspectives of the idea of computation. CSE 130 is an introduction to some of these perspectives, as well as the fundamental concepts of languages.

+

We shall focus on two different paradigms – functional and logic programming as embodied in Haskell and Prolog. Many students will be encountering these paradigms, languages and idioms for the first time. As with spoken languages, these are best absorbed by immersing yourself in the different environments and practicing your skills by experimentation.

+

Integrity of Scholarship

+

University rules on integrity of scholarship will be strictly enforced. By taking this course, you implicitly agree to abide by the UCSD Policy on Integrity of Scholarship described here. In particular,

+
+

all academic work will be done by the student to whom it is assigned, without unauthorized aid of any kind.

+
+

Incidents which violate the University’s rules on integrity of scholarship will be taken seriously. In addition to receiving a zero (0) on the assignment/exam in question, students may also face other penalties, up to and including, expulsion from the University. Should you have any doubts about the moral and/or ethical implications of an activity regarding the course, please see the instructor.

+

Research

+

Your class work might be used for research purposes. For example, we may use anonymized student assignments to design algorithms or build tools to help programmers. Any student who wishes to opt out can contact the instructor or TA to do so after final grades have been issued. This has no impact on your grade in any manner.

+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/lectures.html b/docs/lectures.html new file mode 100644 index 0000000..dd392d2 --- /dev/null +++ b/docs/lectures.html @@ -0,0 +1,435 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

Lecture Notes

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

The live-edited code for each lecture can be found here.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DateTopicNotes
1/4Hello, World!html
1/6The Lambda Calculushtml
1/11contd.pdf
1/13A crash course in Haskellhtml pdf
1/18contd.
1/20contd.
1/27Datatypes & Recursionhtml pdf
2/1contd.contd.
2/3contd.contd.
2/8Higher Order Functionshtml pdf
2/10midterm |
2/15contd. | contd.contd. |
2/17Environments & Closureshtml pdf
2/22contd.contd.
2/24Lexing and Parsinghtml pdf
3/1Overloading & Type Classeshtml [pdf]
3/3contd.
3/8Functors & Monadshtml
3/10Hello, world! (IO Monad)html
3/15final |
+ +
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/lectures/00-hello.html b/docs/lectures/00-hello.html new file mode 100644 index 0000000..278d601 --- /dev/null +++ b/docs/lectures/00-hello.html @@ -0,0 +1,652 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

Hello, world!

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

A Programming Language

+

Two Variables

+
    +
  • x, y
  • +
+

Three Operations

+
    +
  • x++
  • +
  • x--
  • +
  • x = 0 ? L1 : L2
  • +
+







+

Example Program

+

(What does it do?)

+
L1: x++
+    y--
+    y = 0 ? L2 : L1
+L2: ...
+







+

The above language is “equivalent to” every PL!

+

But good luck writing

+
    +
  • … QuickSort
  • +
  • … Zelda
  • +
  • … Spotify!
  • +
+







+

So Why Study Programming Languages?

+
+
Federico Fellini
+
+
+

A different language is a different vision of life.

+
+







+

So Why Study Programming Languages?

+
+

The principle of linguistic relativity holds that the structure of a language affects its speakers world view or cognition.

+
+

Or more simply:

+
+

Programming Language shapes Programming Thought.

+
+

Language affects how ideas and computation are expressed

+







+

Course Goals

+
+
130 Brain
+
+







+

New languages come (and go …)

+

There was no

+
    +
  • Java 25 years ago
  • +
  • C# 20 years ago
  • +
  • Rust 10 years ago
  • +
  • WebAssembly 2 years ago
  • +
+







+

What is CSE 130 about?

+
    +
  • Concepts in programming languages
  • +
  • Programming paradigms
  • +
  • Language design and implementation
  • +
+













+

Goal: Learn the Anatomy of PL

+
+
Anatomy
+
+
    +
  • What makes a programming language?
  • +
  • Which features are fundamental and which are syntactic sugar?
  • +
+







+

Goal: Learn New Languages / Constructs

+
+
Musical Score
+
+

New ways to describe and organize computation, to create programs that are:

+
    +
  • Correct
  • +
  • Readable
  • +
  • Extendable
  • +
  • Reusable
  • +
+







+

Goal: How to Design new Languages

+

New hot languages being designed in industry as we speak:

+
    +
  • Flow, React @ Facebook
  • +
  • Rust @ Mozilla
  • +
  • TypeScript @ Microsoft
  • +
  • Swift @ Apple
  • +
  • WebAssembly @ Google + Mozilla + Microsoft
  • +
+

Buried in every large system is a (domain-specific) language

+
    +
  • DB: SQL
  • +
  • Word, Excel: Formulas, Macros, VBScript
  • +
  • Emacs: LISP
  • +
  • Latex, shell scripts, makefiles, …
  • +
+

If you work on a large system, you will design a new PL!

+







+

Goal: Enable You To Choose Right PL

+

But isn’t that decided by

+
    +
  • Libraries
  • +
  • Standards
  • +
  • Hiring
  • +
  • Your Boss?!
  • +
+

Yes.

+

My goal: Educate tomorrow’s leaders so you’ll make informed choices.

+







+

What is CSE 130 not about?

+

Learning…

+
    +
  • JavaScript in April
  • +
  • Haskell in May
  • +
  • C++ in June
  • +
+

etc.

+







+

Who am I?

+

Ranjit Jhala

+
    +
  • Professor at CSE since 2005

  • +
  • Research: Tools and Techniques to make programs better

  • +
+









+

The Crew

+

Teaching Assistants

+ +

Tutors

+ +











+

Course Syllabus

+

Functional Programming

+
    +
  • Lambda calculus (2 weeks)
  • +
  • Haskell (6 weeks)
  • +
+

Logic Programming

+
    +
  • Prolog (2 weeks)
  • +
+







+

QuickSort in C

+
void sort(int arr[], int beg, int end){
+  if (end > beg + 1){
+    int piv = arr[beg];
+    int l = beg + 1;
+    int r = end;
+    while (l != r-1)
+       if(arr[l] <= piv) l++;
+       else swap(&arr[l], &arr[r--]);
+    if(arr[l]<=piv && arr[r]<=piv)
+       l=r+1;
+    else if(arr[l]<=piv && arr[r]>piv)
+       {l++; r--;}
+    else if (arr[l]>piv && arr[r]<=piv)
+       swap(&arr[l++], &arr[r--]);
+    else r=l-1;
+    swap(&arr[r--], &arr[beg]);
+    sort(arr, beg, r);
+    sort(arr, l, end);
+  }
+}
+







+

QuickSort in Haskell

+
sort []     = []
+sort (x:xs) = sort ls ++ [x] ++ sort rs
+  where
+    ls      = [ l | l <- xs, l <= x ]
+    rs      = [ r | r <- xs, x <  r ]
+

(not a wholly fair comparison…)

+













+

Course Logistics

+

webpage

+
    +
  • Calendar
  • +
  • Lecture notes
  • +
  • Programming assignments
  • +
+

piazza

+
    +
  • Go-to place if you have a question or need help
  • +
+







+

Grading

+
    +
  • 45% Assignments
  • +
  • 25% Midterm (Thu Feb 10)
  • +
  • 30% Final (Tue Mar 15)
  • +
  • 05% Piazza Extra Credit +
      +
    • To top 20 best participants
    • +
  • +
+







+

Assignments

+

6 programming assignments

+
    +
  • Released online
  • +
  • At least a week before due date
  • +
  • On github
  • +
+

Eight late days

+
    +
  • used as whole unit
  • +
  • 5 mins late = 1 late day
  • +
+

Groups of two can do assignments together

+
    +
  • On ieng6 or your own machine
  • +
  • Submit individually (via github)
  • +
+







+

Exams

+
    +
  • Synchronous, must be done at allotted time and location

  • +
  • 2-sided “cheat sheet”

  • +
  • The final is cumulative

  • +
  • Midterm grade is calculated as midterm > 0 ? max(final, midterm) : 0

    +
      +
    • you get a second chance if you don’t do well on the midterm
    • +
    • you must take the midterm and the final
    • +
  • +
+







+ +

Your Resources

+

Discussion section

+
    +
  • Wednesday 08:00-08:50am (see CANVAS for ZOOM)
  • +
+

Office hours

+
    +
  • Every day, check CANVAS calendar
  • +
+

Piazza

+
    +
  • We answer during work hours and office hours (M-F)
  • +
+

No text

+
    +
  • Online lecture notes and links
  • +
+







+

Academic Integrity

+

Programming assignments: do not copy from classmates or from previous years

+

Exams done alone

+
    +
  • Zero Tolerance
  • +
  • Offenders punished ruthlessly
  • +
  • Please see academic integrity statement
  • +
+







+

Students with Disabilites

+

Students requesting accommodations for this course due to a disability or current functional limitation must provide a current Authorization for Accommodation (AFA) letter issued by the Office for Students with Disabilities (OSD) which is located in University Center 202 behind Center Hall.

+

Students are required to present their AFA letters to Faculty (please make arrangements to contact me privately) and to the CSE OSD Liaison Christina Rontell in advance so that accommodations may be arranged.

+







+

Diversity and Inclusion

+

Goal

+
    +
  • Create a diverse and inclusive learning environment
  • +
  • Where all students feel comfortable and can thrive.
  • +
  • Please let us know if there is a way to make you feel more included
  • +
  • In person, via email/discussion boar
  • +
+

Expectations

+
    +
  • Students will honor and respect your classmates!
  • +
  • Abide by the UCSD Principles of Community
  • +
  • Understand that others’ backgrounds, perspectives and experiences will be different
  • +
  • Help build an environment where everyone is respected and comfortable.
  • +
+







+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/lectures/01-lambda-notes.html b/docs/lectures/01-lambda-notes.html new file mode 100644 index 0000000..81243e3 --- /dev/null +++ b/docs/lectures/01-lambda-notes.html @@ -0,0 +1,1370 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

Lambda Calculus

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

Your Favorite Language

+

Probably has lots of features:

+
    +
  • Assignment (x = x + 1)
  • +
  • Booleans, integers, characters, strings, …
  • +
  • Conditionals
  • +
  • Loops
  • +
  • return, break, continue
  • +
  • Functions
  • +
  • Recursion
  • +
  • References / pointers
  • +
  • Objects and classes
  • +
  • Inheritance
  • +
  • +
+

Which ones can we do without?

+

What is the smallest universal language?

+


















+

What is computable?

+

Before 1930s

+

Informal notion of an effectively calculable function:

+
+
can be computed by a human with pen and paper, following an algorithm
+
+





+

1936: Formalization

+

What is the smallest universal language?

+
+
Alan Turing
+
+



+
+
Alonzo Church
+
+














+

The Next 700 Languages

+
+
Peter Landin
+
+
+

Whatever the next 700 languages turn out to be, they will surely be variants of lambda calculus.

+
+

Peter Landin, 1966

+















+

The Lambda Calculus

+

Has one feature:

+
    +
  • Functions
  • +
+











+

No, really

+
    +
  • Assignment (x = x + 1)
  • +
  • Booleans, integers, characters, strings, …
  • +
  • Conditionals
  • +
  • Loops
  • +
  • return, break, continue
  • +
  • Functions
  • +
  • Recursion
  • +
  • References / pointers
  • +
  • Objects and classes
  • +
  • Inheritance
  • +
  • Reflection
  • +
+











+

More precisely, only thing you can do is:

+
    +
  • Define a function
  • +
  • Call a function
  • +
+











+

Describing a Programming Language

+
    +
  • Syntax: what do programs look like?
  • +
  • Semantics: what do programs mean? +
      +
    • Operational semantics: how do programs execute step-by-step?
    • +
  • +
+











+

Syntax: What Programs Look Like

+


+
e ::= x           -- variable 'x'
+    | (\x -> e)   -- function that takes a parameter 'x' and returns 'e'
+    | (e1 e2)     -- call (function) 'e1' with argument 'e2'
+


+

Programs are expressions e (also called λ-terms) of one of three kinds:

+
    +
  • Variable +
      +
    • x, y, z
    • +
  • +
  • Abstraction (aka nameless function definition) +
      +
    • (\x -> e)
    • +
    • x is the formal parameter, e is the body
    • +
    • “for any x compute e
    • +
  • +
  • Application (aka function call) +
      +
    • (e1 e2)
    • +
    • e1 is the function, e2 is the argument
    • +
    • in your favorite language: e1(e2)
    • +
  • +
+

(Here each of e, e1, e2 can itself be a variable, abstraction, or application)

+











+

Examples

+
(\x -> x)             -- The identity function (id) that returns its input
+
+(\x -> (\y -> y))     -- A function that returns (id)
+
+(\f -> (f (\x -> x))) -- A function that applies its argument to id 
+













+

QUIZ

+

Which of the following terms are syntactically incorrect?

+

A. (\(\x -> x) -> y)

+

B. (\x -> (x x))

+

C. (\x -> (x (y x)))

+

D. A and C

+

E. all of the above

+


+











+

Examples

+
(\x -> x)             -- The identity function (id) that returns its input
+
+(\x -> (\y -> y))     -- A function that returns (id)
+
+(\f -> (f (\x -> x))) -- A function that applies its argument to id 
+


+

How do I define a function with two arguments?

+
    +
  • e.g. a function that takes x and y and returns y?
  • +
+













+
(\x -> (\y -> y))   -- A function that returns the identity function
+                    -- OR: a function that takes two arguments
+                    -- and returns the second one!
+













+

How do I apply a function to two arguments?

+
    +
  • e.g. apply (\x -> (\y -> y)) to apple and banana?
  • +
+













+
(((\x -> (\y -> y)) apple) banana) -- first apply to apple,
+                                   -- then apply the result to banana
+












+

Syntactic Sugar

+



+ + + + + + + + + + + + + + + + + + + + + +
instead ofwe write
\x -> (\y -> (\z -> e))\x -> \y -> \z -> e
\x -> \y -> \z -> e\x y z -> e
(((e1 e2) e3) e4)e1 e2 e3 e4
+



+
\x y -> y     -- A function that that takes two arguments
+              -- and returns the second one...
+              
+(\x y -> y) apple banana -- ... applied to two arguments
+













+

Semantics : What Programs Mean

+


+

How do I “run” / “execute” a λ-term?

+


+

Think of middle-school algebra:

+
    (1 + 2) * ((3 * 8) - 2)
+    => 
+    3 * ((3 * 8) - 2)
+    => 
+    3 * (24 - 2)
+    => 
+    3 * 22
+    =>
+    66
+
    (1 + 2) * ((3 * 8) - 2)
+ == 
+    3      * ((3 * 8) - 2)
+ == 
+    3      * ( 24     - 2)
+ == 
+    3      *  22
+ == 
+    66
+


+

Execute = rewrite step-by-step

+
    +
  • Following simple rules
  • +
  • until no more rules apply
  • +
+













+

Rewrite Rules of Lambda Calculus

+


+
    +
  1. β-step (aka function call)
  2. +
  3. α-step (aka renaming formals)
  4. +
+

A. I have used an IDE that has “refactoring” B. I don’t know what either “IDE” or “refactoring” is

+


+

But first we have to talk about scope

+

(-> e)

+







+

Semantics: Scope of a Variable

+

The part of a program where a variable is visible

+

In the expression (\x -> e)

+
    +
  • x is the newly introduced variable

  • +
  • e is the scope of x

  • +
  • any occurrence of x in (\x -> e) is bound (by the binder \x)

  • +
+


+

For example, x is bound in:

+
  (\x -> x)
+
+  -- A    -- B
+  (\x -> (\y -> x))
+
+  -- A            -- B
+  fun(x) { return fun(y) { return x }}
+

where is x bound? (A) or (B)

+



+

An occurrence of x in e is free if it’s not bound by an enclosing λ-abstraction

+


For example, x is free in:

+
  (x y)                  -- no binders at all!  
+
+  (\y -> (x y))          -- no \x binder
+
+  ((\x -> (\y -> y)) x)  -- x is outside the scope of the \x binder;
+                         -- intuition: it's not "the same" x
+












+

QUIZ

+
                                                1  2 
+

Is x bound or free in the expression ((\x -> x) x)?

+

A. first occurrence is bound, second is bound

+

B. first occurrence is bound, second is free

+

C. first occurrence is free, second is bound

+

D. first occurrence is free, second is free

+


+












+

EXERCISE: Free Variables

+

An variable x is free in e if there exists a free occurrence of x in e

+

FREEVARS(x y) = {x, y}

+

FREEVARS(\x -> x) = { }

+

FREEVARS(((\x -> x) y)) = {y}

+

FREEVARS(x) = { x }

+

FREEVARS(-> e) = FREEVARS(e) - {x}

+

FREEVARS(e1 e2) = FREEVARS(e1) + FREEVARS(e2)

+


+

We can formally define the set of all free variables in a term like so:

+
FV(x)       = ???
+FV(\x -> e) = ???
+FV(e1 e2)   = ???
+













+

Closed Expressions

+

If e has no free variables it is said to be closed

+
    +
  • Closed expressions are also called combinators
  • +
+

(\horse -> horse)

+



+

What is the shortest closed expression?

+













+

Rewrite Rules of Lambda Calculus

+


+
    +
  1. β-step (aka function call)
  2. +
  3. α-step (aka renaming formals)
  4. +
+

SCOPE & FUN-CALL?

+













+

Semantics: Redex

+

A redex is a term of the form

+


+
  ((\x -> e1) e2)
+

A function (\x -> e1)

+
    +
  • x is the parameter
  • +
  • e1 is the returned expression
  • +
+

Applied to an argument e2

+
    +
  • e2 is the argument
  • +
+













+

Semantics: β-Reduction

+


+

A redex b-steps to another term …

+
  (\x -> e1) e2   =b>   e1[x := e2]
+


+

where e1[x := e2] means

+

e1 with all free occurrences of x replaced with e2

+



+

Computation by search-and-replace:

+

If you see an -abstraction applied to an argument,

+
    +
  • In the body of the abstraction
  • +
  • Replace all free occurrences of the formal by that argument
  • +
+

We say that (\x -> e1) e2 β-steps to e1[x := e2]

+













+

Redex Examples

+


+
((\x -> x) apple)     
+
+=b> apple
+

Is this right? Ask Elsa

+













+

QUIZ

+


+
((\x -> (\y -> y)) apple)
+
+
+
+=b> ???
+

(-> e1) e2

+

x = x e1 = (-> y) e2 = apple

+

e1[x:= e2] ==> (-> y)

+

A. apple

+

B. \y -> apple

+

C. \x -> apple

+

D. \y -> y

+

E. \x -> y

+


+












+

QUIZ

+


+
(\x -> (((y x) y) x)) apple
+=b> ???
+

A. (((apple apple) apple) apple)

+

B. (((y apple) y) apple)

+

C. (((y y) y) y)

+

D. apple

+












+

QUIZ

+


+
(\x -> (x (\x -> x))) apple
+
+=b> ???
+

= x

+

e1 = (x (-> x))

+

e2 = apple

+

e1[x := apple] = apple (-> x)

+

A. (apple (\x -> x))

+

B. (apple (\apple -> apple))

+

C. (apple (\x -> apple))

+

D. apple

+

E. (\x -> x)

+


+












+

EXERCISE

+

What is a λ-term fill_this_in such that

+


+
((\x -> banana) apple)
+=b> banana
+

ELSA: https://goto.ucsd.edu/elsa/index.html

+

Click here to try this exercise

+












+

A Tricky One

+


+
((\x -> (\y   -> x)) y)
+
+
+
+=b> \y -> y
+

Is this right?

+
    +
  1. Yes its fine relax

  2. +
  3. oo something fishy!

  4. +
+













+

Something is Fishy

+


+
(\x -> (\y -> x)) y
+
+=b> (\y -> y)
+

Is this right?

+

Problem: The free y in the argument has been captured by \y in body!

+


+

Solution: Ensure that formals in the body are different from free-variables of argument!

+












+

Capture-Avoiding Substitution

+

We have to fix our definition of β-reduction:

+
  (\x -> e1) e2   =b>   e1[x := e2]
+


where e1[x := e2] means e1 with all free occurrences of x replaced with e2

+
    +
  • e1 with all free occurrences of x replaced with e2
  • +
  • as long as no free variables of e2 get captured
  • +
+


+

Formally:

+
x[x := e]            = e
+
+y[x := e]            = y                 -- as x /= y
+
+(e1 e2)[x := e]      = (e1[x := e]) (e2[x := e])
+
+(\x -> e1)[x := e]   = (\x -> e1)        -- Q: Why leave `e1` unchanged?
+
+(\y -> e1)[x := e] 
+  | not (y in FV(e)) = \y -> e1[x := e]
+

Oops, but what to do if y is in the free-variables of e?

+
    +
  • i.e. if \y -> ... may capture those free variables?
  • +
+












+

Rewrite Rules of Lambda Calculus

+


+
    +
  1. β-step (aka function call)
  2. +
  3. α-step (aka renaming formals)
  4. +
+













+

Semantics: α-Renaming

+


+
  \x -> e   =a>   \y -> e[x := y]
+    where not (y in FV(e))
+


+
    +
  • We rename a formal parameter x to y

  • +
  • By replace all occurrences of x in the body with y

  • +
  • We say that \x -> e α-steps to \y -> e[x := y]

  • +
+



+

Example:

+
(\x -> x)   =a>   (\y -> y)   =a>    (\z -> z) =a> (\apple -> apple)
+

All these expressions are α-equivalent

+




+

What’s wrong with these?

+
-- (A)
+(\f -> (f x))    =a>   (\x -> (x x))
+
-- (B)
+((\x -> (\y -> y)) y)   =a>   ((\x -> (\z -> z)) z)
+














+

Tricky Example Revisited

+


+
    ((\x -> (\y -> x)) y)
+                                -- rename 'y' to 'z' to avoid capture
+    =a> ((\x -> (\z -> x)) y)
+                                -- now do b-step without capture!
+    =b> (\z -> y)
+



To avoid getting confused,

+
    +
  • you can always rename formals,

  • +
  • so different variables have different names!

  • +
+













+

Normal Forms

+

Recall redex is a λ-term of the form

+

((\x -> e1) e2)

+

A λ-term is in normal form if it contains no redexes.

+














+

QUIZ

+

((-> e1) e2)

+

Which of the following term are not in normal form ? i.e. Which of the following term HAS A REDEX?

+

A. x

+

B. (x y)

+

C. ((\x -> x) y) – redex? YES! ((-> E1) E2)

+

D. (x (\y -> y)) – redex? NO! ((-> E1) E2)

+

E. C and D

+













+

Semantics: Evaluation

+

A λ-term e evaluates to e' if

+
    +
  1. There is a sequence of steps
  2. +
+
e =?> e_1 =?> ... =?> e_N =?> e'
+

where each =?> is either =a> or =b> and N >= 0

+
    +
  1. e' is in normal form
  2. +
+







+

Examples of Evaluation

+
((\x -> banana) apple)
+
+  =b> banana
+


+
(\f -> f (\x -> x)) (\x -> x)
+
+  =?> ???
+


+
(\x -> x x) (\x -> x)
+  =?> ???
+







+

Elsa shortcuts

+

Named λ-terms:

+
let ID = (\x -> x)  -- abbreviation for (\x -> x)
+



+

To substitute name with its definition, use a =d> step:

+
(ID apple)
+  =d> ((\x -> x) apple)  -- expand definition
+  =b> apple              -- beta-reduce
+



+

Evaluation:

+
    +
  • e1 =*> e2: e1 reduces to e2 in 0 or more steps +
      +
    • where each step is =a>, =b>, or =d>
    • +
  • +
  • e1 =~> e2: e1 evaluates to e2 and e2 is in normal form
  • +
+













+

EXERCISE

+

Fill in the definitions of FIRST, SECOND and THIRD such that you get the following behavior in elsa

+
let FIRST  = fill_this_in
+let SECOND = fill_this_in
+let THIRD  = fill_this_in
+
+eval ex1 : 
+  (((FIRST apple) banana) orange)
+  =*> apple 
+
+eval ex2 : 
+  (((SECOND apple) banana) orange)
+  =*> banana 
+  
+eval ex3 : 
+  (((THIRD apple) banana) orange)
+  =*> orange
+

ELSA: https://goto.ucsd.edu/elsa/index.html

+

Click here to try this exercise

+

Non-Terminating Evaluation

+
((\x -> (x x)) (\x -> (x x)))
+
+  =b> ((\x -> (x x)) (\x -> (x x)))
+

Some programs loop back to themselves … never reduce to a normal form!

+

This combinator is called Ω

+







+

What if we pass Ω as an argument to another function?

+
let OMEGA = ((\x -> (x x)) (\x -> (x x)))
+
+((\x -> (\y -> y)) OMEGA)
+

Does this reduce to a normal form? Try it at home!

+










+

Programming in λ-calculus

+

Real languages have lots of features

+
    +
  • Booleans
  • +
  • Records (structs, tuples)
  • +
  • Numbers
  • +
  • Lists
  • +
  • Functions [we got those]
  • +
  • Recursion
  • +
+

Lets see how to encode all of these features with the λ-calculus.

+













+

Syntactic Sugar

+



+ + + + + + + + + + + + + + + + + + + + + +
instead ofwe write
\x -> (\y -> (\z -> e))\x -> \y -> \z -> e
\x -> \y -> \z -> e\x y z -> e
(((e1 e2) e3) e4)e1 e2 e3 e4
+



+
\x y -> y     -- A function that that takes two arguments
+              -- and returns the second one...
+              
+(\x y -> y) apple banana -- ... applied to two arguments
+













+

λ-calculus: Booleans

+


+

How can we encode Boolean values (TRUE and FALSE) as functions?

+
let TRUE  = \x1 x2 -> x1
+let FALSE = \x1 x2 -> x2
+let ITE   = \b x1 x2 -> b x1 x2
+ITE TRUE e1 e2  ==> e1
+ITE FALSE e1 e2 ==> e2
+


+

Well, what do we do with a Boolean b?

+













+

Make a binary choice

+
    +
  • if b then e1 else e2
  • +
+




+

Booleans: API

+

We need to define three functions

+
let TRUE  = ???
+let FALSE = ???
+let ITE   = \b x y -> ???  -- if b then x else y
+

such that

+
ITE TRUE apple banana =~> apple
+ITE FALSE apple banana =~> banana
+

(Here, let NAME = e means NAME is an abbreviation for e)

+













+

Booleans: Implementation

+
let TRUE  = \x y -> x        -- Returns its first argument
+let FALSE = \x y -> y        -- Returns its second argument
+let ITE   = \b x y -> b x y  -- Applies condition to branches
+                             -- (redundant, but improves readability)
+













+

Example: Branches step-by-step

+
eval ite_true:
+  ITE TRUE e1 e2
+  =d> (\b x y -> b    x  y) TRUE e1 e2    -- expand def ITE  
+  =b>   (\x y -> TRUE x  y)      e1 e2    -- beta-step
+  =b>     (\y -> TRUE e1 y)         e2    -- beta-step
+  =b>            TRUE e1 e2               -- expand def TRUE
+  =d>     (\x y -> x) e1 e2               -- beta-step
+  =b>       (\y -> e1)   e2               -- beta-step
+  =b> e1
+







+

Example: Branches step-by-step

+

Now you try it!

+

Can you fill in the blanks to make it happen?

+
eval ite_false:
+  ITE FALSE e1 e2
+
+  -- fill the steps in!
+
+  =b> e2  
+













+

EXERCISE: Boolean Operators

+

ELSA: https://goto.ucsd.edu/elsa/index.html Click here to try this exercise

+

Now that we have ITE it’s easy to define other Boolean operators:

+
let NOT = \b     -> ???
+let OR  = \b1 b2 -> ???
+let AND = \b1 b2 -> ???
+

When you are done, you should get the following behavior:

+
eval ex_not_t:
+  NOT TRUE =*> FALSE
+  
+eval ex_not_f:
+  NOT FALSE =*> TRUE 
+  
+eval ex_or_ff:
+  OR FALSE FALSE =*> FALSE
+
+eval ex_or_ft:
+  OR FALSE TRUE =*> TRUE
+  
+eval ex_or_ft:
+  OR TRUE FALSE =*> TRUE
+
+eval ex_or_tt:
+  OR TRUE TRUE =*> TRUE
+  
+eval ex_and_ff:
+  AND FALSE FALSE =*> FALSE
+
+eval ex_and_ft:
+  AND FALSE TRUE =*> FALSE
+  
+eval ex_and_ft:
+  AND TRUE FALSE =*> FALSE
+
+eval ex_and_tt:
+  AND TRUE TRUE =*> TRUE
+












+

Programming in λ-calculus

+
    +
  • Booleans [done]
  • +
  • Records (structs, tuples)
  • +
  • Numbers
  • +
  • Lists
  • +
  • Functions [we got those]
  • +
  • Recursion
  • +
+













+

λ-calculus: Records

+

Let’s start with records with two fields (aka pairs)

+

What do we do with a pair?

+
    +
  1. Pack two items into a pair, then
  2. +
  3. Get first item, or
  4. +
  5. Get second item.
  6. +
+















+

Pairs : API

+

We need to define three functions

+
let PAIR = \x y -> ???    -- Make a pair with elements x and y 
+                          -- { fst : x, snd : y }
+let FST  = \p -> ???      -- Return first element 
+                          -- p.fst
+let SND  = \p -> ???      -- Return second element
+                          -- p.snd
+

such that

+
eval ex_fst: 
+  FST (PAIR apple banana) =*> apple
+
+eval ex_snd:
+  SND (PAIR apple banana) =*> banana
+













+

Pairs: Implementation

+

A pair of x and y is just something that lets you pick between x and y!

+
let PAIR = \x y -> (\b -> ITE b x y)
+

i.e. PAIR x y is a function that

+
    +
  • takes a boolean and returns either x or y
  • +
+

We can now implement FST and SND by “calling” the pair with TRUE or FALSE

+
let FST  = \p -> p TRUE   -- call w/ TRUE, get first value
+let SND  = \p -> p FALSE  -- call w/ FALSE, get second value
+













+

EXERCISE: Triples

+

How can we implement a record that contains three values?

+

ELSA: https://goto.ucsd.edu/elsa/index.html

+

Click here to try this exercise

+
let TRIPLE = \x y z -> ???
+let FST3   = \t -> ???
+let SND3   = \t -> ???
+let THD3   = \t -> ???
+
+eval ex1:
+  FST3 (TRIPLE apple banana orange)
+  =*> apple
+
+eval ex2:
+  SND3 (TRIPLE apple banana orange)
+  =*> banana 
+
+eval ex3:
+  THD3 (TRIPLE apple banana orange)
+  =*> orange
+













+

Programming in λ-calculus

+
    +
  • Booleans [done]
  • +
  • Records (structs, tuples) [done]
  • +
  • Numbers
  • +
  • Lists
  • +
  • Functions [we got those]
  • +
  • Recursion
  • +
+













+

λ-calculus: Numbers

+

Let’s start with natural numbers (0, 1, 2, …)

+

What do we do with natural numbers?

+
    +
  • Count: 0, inc
  • +
  • Arithmetic: dec, +, -, *
  • +
  • Comparisons: ==, <=, etc
  • +
+













+

Natural Numbers: API

+

We need to define:

+
    +
  • A family of numerals: ZERO, ONE, TWO, THREE, …
  • +
  • Arithmetic functions: INC, DEC, ADD, SUB, MULT
  • +
  • Comparisons: IS_ZERO, EQ
  • +
+

Such that they respect all regular laws of arithmetic, e.g.

+
IS_ZERO ZERO       =~> TRUE
+IS_ZERO (INC ZERO) =~> FALSE
+INC ONE            =~> TWO
+...
+













+

Natural Numbers: Implementation

+

Church numerals: a number N is encoded as a combinator that calls a function on an argument N times

+
let ONE   = \f x -> f x
+let TWO   = \f x -> f (f x)
+let THREE = \f x -> f (f (f x))
+let FOUR  = \f x -> f (f (f (f x)))
+let FIVE  = \f x -> f (f (f (f (f x))))
+let SIX   = \f x -> f (f (f (f (f (f x)))))
+...
+













+

QUIZ: Church Numerals

+

Which of these is a valid encoding of ZERO ?

+
    +
  • A: let ZERO = \f x -> x

  • +
  • B: let ZERO = \f x -> f

  • +
  • C: let ZERO = \f x -> f x

  • +
  • D: let ZERO = \x -> x

  • +
  • E: None of the above

  • +
+




+

Does this function look familiar?

+












+

λ-calculus: Increment

+
-- Call `f` on `x` one more time than `n` does
+let INC   = \n -> (\f x -> ???)
+













+

Example:

+
eval inc_zero :
+  INC ZERO
+  =d> (\n f x -> f (n f x)) ZERO
+  =b> \f x -> f (ZERO f x)
+  =*> \f x -> f x
+  =d> ONE
+













+

EXERCISE

+

Fill in the implementation of ADD so that you get the following behavior

+

Click here to try this exercise

+
let ZERO = \f x -> x
+let ONE  = \f x -> f x
+let TWO  = \f x -> f (f x)
+let INC  = \n f x -> f (n f x)
+
+let ADD  = fill_this_in 
+
+eval add_zero_zero: 
+  ADD ZERO ZERO =~> ZERO
+
+eval add_zero_one: 
+  ADD ZERO ONE =~> ONE
+
+eval add_zero_two: 
+  ADD ZERO TWO =~> TWO 
+
+eval add_one_zero: 
+  ADD ONE ZERO =~> ONE
+
+eval add_one_zero: 
+  ADD ONE ONE =~> TWO
+
+eval add_two_zero: 
+  ADD TWO ZERO =~> TWO  
+

QUIZ

+

How shall we implement ADD?

+

A. let ADD = \n m -> n INC m

+

B. let ADD = \n m -> INC n m

+

C. let ADD = \n m -> n m INC

+

D. let ADD = \n m -> n (m INC)

+

E. let ADD = \n m -> n (INC m)

+













+

λ-calculus: Addition

+
--  Call `f` on `x` exactly `n + m` times
+let ADD = \n m -> n INC m
+




+

Example:

+
eval add_one_zero :
+  ADD ONE ZERO
+  =~> ONE
+













+

QUIZ

+

How shall we implement MULT?

+

A. let MULT = \n m -> n ADD m

+

B. let MULT = \n m -> n (ADD m) ZERO

+

C. let MULT = \n m -> m (ADD n) ZERO

+

D. let MULT = \n m -> n (ADD m ZERO)

+

E. let MULT = \n m -> (n ADD m) ZERO

+













+

λ-calculus: Multiplication

+
--  Call `f` on `x` exactly `n * m` times
+let MULT = \n m -> n (ADD m) ZERO
+




+

Example:

+
eval two_times_three :
+  MULT TWO ONE
+  =~> TWO
+













+

Programming in λ-calculus

+
    +
  • Booleans [done]
  • +
  • Records (structs, tuples) [done]
  • +
  • Numbers [done]
  • +
  • Lists
  • +
  • Functions [we got those]
  • +
  • Recursion
  • +
+













+

λ-calculus: Lists

+

Lets define an API to build lists in the λ-calculus.

+

An Empty List

+
NIL
+

Constructing a list

+

A list with 4 elements

+
CONS apple (CONS banana (CONS cantaloupe (CONS dragon NIL)))
+

intuitively CONS h t creates a new list with

+
    +
  • head h
  • +
  • tail t
  • +
+

Destructing a list

+
    +
  • HEAD l returns the first element of the list
  • +
  • TAIL l returns the rest of the list
  • +
+
HEAD (CONS apple (CONS banana (CONS cantaloupe (CONS dragon NIL))))
+=~> apple
+
+TAIL (CONS apple (CONS banana (CONS cantaloupe (CONS dragon NIL))))
+=~> CONS banana (CONS cantaloupe (CONS dragon NIL)))
+













+

λ-calculus: Lists

+
let NIL  = ???
+let CONS = ???
+let HEAD = ???
+let TAIL = ???
+
+eval exHd:
+  HEAD (CONS apple (CONS banana (CONS cantaloupe (CONS dragon NIL))))
+  =~> apple
+
+eval exTl 
+  TAIL (CONS apple (CONS banana (CONS cantaloupe (CONS dragon NIL))))
+  =~> CONS banana (CONS cantaloupe (CONS dragon NIL)))
+













+

EXERCISE: Nth

+

Write an implementation of GetNth such that

+
    +
  • GetNth n l returns the n-th element of the list l
  • +
+

Assume that l has n or more elements

+
let GetNth = ???
+
+eval nth1 :
+  GetNth ZERO (CONS apple (CONS banana (CONS cantaloupe NIL)))
+  =~> apple 
+
+eval nth1 :
+  GetNth ONE (CONS apple (CONS banana (CONS cantaloupe NIL)))
+  =~> banana
+
+eval nth2 :
+  GetNth TWO (CONS apple (CONS banana (CONS cantaloupe NIL)))
+  =~> cantaloupe
+

Click here to try this in elsa

+









+

λ-calculus: Recursion

+


+

I want to write a function that sums up natural numbers up to n:

+
let SUM = \n -> ...  -- 0 + 1 + 2 + ... + n
+

such that we get the following behavior

+
eval exSum0: SUM ZERO  =~> ZERO
+eval exSum1: SUM ONE   =~> ONE
+eval exSum2: SUM TWO   =~> THREE
+eval exSum3: SUM THREE =~> SIX
+

Can we write sum using Church Numerals?

+

Click here to try this in Elsa

+











+

QUIZ

+

You can write SUM using numerals but its tedious.

+

Is this a correct implementation of SUM?

+
let SUM = \n -> ITE (ISZ n) 
+            ZERO 
+            (ADD n (SUM (DEC n)))
+

A. Yes

+

B. No

+











+

No!

+
    +
  • Named terms in Elsa are just syntactic sugar
  • +
  • To translate an Elsa term to λ-calculus: replace each name with its definition
  • +
+
\n -> ITE (ISZ n) 
+        ZERO 
+        (ADD n (SUM (DEC n))) -- But SUM is not yet defined!
+



+

Recursion:

+
    +
  • Inside this function
  • +
  • Want to call the same function on DEC n
  • +
+



+

Looks like we can’t do recursion!

+
    +
  • Requires being able to refer to functions by name,
  • +
  • But λ-calculus functions are anonymous.
  • +
+

Right?

+













+

λ-calculus: Recursion

+

Think again!

+



+

Recursion:

+

Instead of

+
    +
  • Inside this function I want to call the same function on DEC n
  • +
+

Lets try

+
    +
  • Inside this function I want to call some function rec on DEC n
  • +
  • And BTW, I want rec to be the same function
  • +
+



+

Step 1: Pass in the function to call “recursively”

+
let STEP = 
+  \rec -> \n -> ITE (ISZ n) 
+                  ZERO 
+                  (ADD n (rec (DEC n))) -- Call some rec
+



+

Step 2: Do some magic to STEP, so rec is itself

+
\n -> ITE (ISZ n) ZERO (ADD n (rec (DEC n)))
+

That is, obtain a term MAGIC such that

+
MAGIC =*> STEP MAGIC 
+













+

λ-calculus: Fixpoint Combinator

+

Wanted: a λ-term FIX such that

+
    +
  • FIX STEP calls STEP with FIX STEP as the first argument:
  • +
+
(FIX STEP) =*> STEP (FIX STEP)
+


+

(In math: a fixpoint of a function f(x) is a point x, such that f(x) = x)

+





+

Once we have it, we can define:

+
let SUM = FIX STEP
+

Then by property of FIX we have:

+
SUM   =*>   FIX STEP  =*>   STEP (FIX STEP)   =*>   STEP SUM
+

and so now we compute:

+
eval sum_two:
+  SUM TWO
+  =*> STEP SUM TWO
+  =*> ITE (ISZ TWO) ZERO (ADD TWO (SUM (DEC TWO)))
+  =*> ADD TWO (SUM (DEC TWO))
+  =*> ADD TWO (SUM ONE)
+  =*> ADD TWO (STEP SUM ONE)
+  =*> ADD TWO (ITE (ISZ ONE) ZERO (ADD ONE (SUM (DEC ONE))))
+  =*> ADD TWO (ADD ONE (SUM (DEC ONE)))
+  =*> ADD TWO (ADD ONE (SUM ZERO))
+  =*> ADD TWO (ADD ONE (ITE (ISZ ZERO) ZERO (ADD ZERO (SUM DEC ZERO)))
+  =*> ADD TWO (ADD ONE (ZERO)) 
+  =*> THREE
+

How should we define FIX???

+













+

The Y combinator

+

Remember Ω?

+
(\x -> x x) (\x -> x x)
+=b> (\x -> x x) (\x -> x x)
+

This is self-replcating code! We need something like this but a bit more involved…

+





+

The Y combinator discovered by Haskell Curry:

+
let FIX   = \stp -> (\x -> stp (x x)) (\x -> stp (x x))
+



+

How does it work?

+
eval fix_step:
+  FIX STEP
+  =d> (\stp -> (\x -> stp (x x)) (\x -> stp (x x))) STEP
+  =b> (\x -> STEP (x x)) (\x -> STEP (x x))
+  =b> STEP ((\x -> STEP (x x)) (\x -> STEP (x x)))
+  --       ^^^^^^^^^^ this is FIX STEP ^^^^^^^^^^^
+






+

That’s all folks, Haskell Curry was very clever.

+

Next week: We’ll look at the language named after him (Haskell)

+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/lectures/01-lambda.html b/docs/lectures/01-lambda.html new file mode 100644 index 0000000..5da9634 --- /dev/null +++ b/docs/lectures/01-lambda.html @@ -0,0 +1,1323 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

Lambda Calculus

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

Your Favorite Language

+

Probably has lots of features:

+
    +
  • Assignment (x = x + 1)
  • +
  • Booleans, integers, characters, strings, …
  • +
  • Conditionals
  • +
  • Loops
  • +
  • return, break, continue
  • +
  • Functions
  • +
  • Recursion
  • +
  • References / pointers
  • +
  • Objects and classes
  • +
  • Inheritance
  • +
  • +
+

Which ones can we do without?

+

What is the smallest universal language?

+


















+

What is computable?

+

Before 1930s

+

Informal notion of an effectively calculable function:

+
+
can be computed by a human with pen and paper, following an algorithm
+
+





+

1936: Formalization

+

What is the smallest universal language?

+
+
Alan Turing
+
+



+
+
Alonzo Church
+
+














+

The Next 700 Languages

+
+
Peter Landin
+
+
+

Whatever the next 700 languages turn out to be, they will surely be variants of lambda calculus.

+
+

Peter Landin, 1966

+















+

The Lambda Calculus

+

Has one feature:

+
    +
  • Functions
  • +
+











+

No, really

+
    +
  • Assignment (x = x + 1)
  • +
  • Booleans, integers, characters, strings, …
  • +
  • Conditionals
  • +
  • Loops
  • +
  • return, break, continue
  • +
  • Functions
  • +
  • Recursion
  • +
  • References / pointers
  • +
  • Objects and classes
  • +
  • Inheritance
  • +
  • Reflection
  • +
+











+

More precisely, only thing you can do is:

+
    +
  • Define a function
  • +
  • Call a function
  • +
+











+

Describing a Programming Language

+
    +
  • Syntax: what do programs look like?
  • +
  • Semantics: what do programs mean? +
      +
    • Operational semantics: how do programs execute step-by-step?
    • +
  • +
+











+

Syntax: What Programs Look Like

+


+
e ::= x           -- variable 'x'
+    | (\x -> e)   -- function that takes a parameter 'x' and returns 'e'
+    | (e1 e2)     -- call (function) 'e1' with argument 'e2'
+


+

Programs are expressions e (also called λ-terms) of one of three kinds:

+
    +
  • Variable +
      +
    • x, y, z
    • +
  • +
  • Abstraction (aka nameless function definition) +
      +
    • (\x -> e)
    • +
    • x is the formal parameter, e is the body
    • +
    • “for any x compute e
    • +
  • +
  • Application (aka function call) +
      +
    • (e1 e2)
    • +
    • e1 is the function, e2 is the argument
    • +
    • in your favorite language: e1(e2)
    • +
  • +
+

(Here each of e, e1, e2 can itself be a variable, abstraction, or application)

+











+

Examples

+
(\x -> x)             -- The identity function (id) that returns its input
+
+(\x -> (\y -> y))     -- A function that returns (id)
+
+(\f -> (f (\x -> x))) -- A function that applies its argument to id 
+













+

QUIZ

+

Which of the following terms are syntactically incorrect?

+

A. (\(\x -> x) -> y)

+

B. (\x -> (x x))

+

C. (\x -> (x (y x)))

+

D. A and C

+

E. all of the above

+


+











+

Examples

+
(\x -> x)             -- The identity function (id) that returns its input
+
+(\x -> (\y -> y))     -- A function that returns (id)
+
+(\f -> (f (\x -> x))) -- A function that applies its argument to id 
+


+

How do I define a function with two arguments?

+
    +
  • e.g. a function that takes x and y and returns y?
  • +
+













+
(\x -> (\y -> y))   -- A function that returns the identity function
+                    -- OR: a function that takes two arguments
+                    -- and returns the second one!
+













+

How do I apply a function to two arguments?

+
    +
  • e.g. apply (\x -> (\y -> y)) to apple and banana?
  • +
+













+
(((\x -> (\y -> y)) apple) banana) -- first apply to apple,
+                                   -- then apply the result to banana
+












+

Syntactic Sugar

+



+ + + + + + + + + + + + + + + + + + + + + +
instead ofwe write
\x -> (\y -> (\z -> e))\x -> \y -> \z -> e
\x -> \y -> \z -> e\x y z -> e
(((e1 e2) e3) e4)e1 e2 e3 e4
+



+
\x y -> y     -- A function that that takes two arguments
+              -- and returns the second one...
+              
+(\x y -> y) apple banana -- ... applied to two arguments
+













+

Semantics : What Programs Mean

+


+

How do I “run” / “execute” a λ-term?

+


+

Think of middle-school algebra:

+
    (1 + 2) * ((3 * 8) - 2)
+ == 
+    3      * ((3 * 8) - 2)
+ == 
+    3      * ( 24     - 2)
+ == 
+    3      *  22
+ == 
+    66
+


+

Execute = rewrite step-by-step

+
    +
  • Following simple rules
  • +
  • until no more rules apply
  • +
+













+

Rewrite Rules of Lambda Calculus

+


+
    +
  1. β-step (aka function call)
  2. +
  3. α-step (aka renaming formals)
  4. +
+


+

But first we have to talk about scope

+







+

Semantics: Scope of a Variable

+

The part of a program where a variable is visible

+

In the expression (\x -> e)

+
    +
  • x is the newly introduced variable

  • +
  • e is the scope of x

  • +
  • any occurrence of x in (\x -> e) is bound (by the binder \x)

  • +
+


+

For example, x is bound in:

+
  (\x -> x)
+
+  (\x -> (\y -> x))
+



+

An occurrence of x in e is free if it’s not bound by an enclosing abstraction

+


For example, x is free in:

+
  (x y)                  -- no binders at all!  
+
+  (\y -> (x y))          -- no \x binder
+
+  ((\x -> (\y -> y)) x)  -- x is outside the scope of the \x binder;
+                         -- intuition: it's not "the same" x
+












+

QUIZ

+

Is x bound or free in the expression ((\x -> x) x)?

+

A. first occurrence is bound, second is bound

+

B. first occurrence is bound, second is free

+

C. first occurrence is free, second is bound

+

D. first occurrence is free, second is free

+


+












+

EXERCISE: Free Variables

+

An variable x is free in e if there exists a free occurrence of x in e

+


+

We can formally define the set of all free variables in a term like so:

+
FV(x)       = ???
+FV(\x -> e) = ???
+FV(e1 e2)   = ???
+













+

Closed Expressions

+

If e has no free variables it is said to be closed

+
    +
  • Closed expressions are also called combinators
  • +
+



+

What is the shortest closed expression?

+













+

Rewrite Rules of Lambda Calculus

+


+
    +
  1. β-step (aka function call)
  2. +
  3. α-step (aka renaming formals)
  4. +
+













+

Semantics: Redex

+

A redex is a term of the form

+


+
  ((\x -> e1) e2)
+

A function (\x -> e1)

+
    +
  • x is the parameter
  • +
  • e1 is the returned expression
  • +
+

Applied to an argument e2

+
    +
  • e2 is the argument
  • +
+













+

Semantics: β-Reduction

+


+

A redex b-steps to another term …

+
  (\x -> e1) e2   =b>   e1[x := e2]
+


+

where e1[x := e2] means

+

e1 with all free occurrences of x replaced with e2

+



+

Computation by search-and-replace:

+

If you see an abstraction applied to an argument,

+
    +
  • In the body of the abstraction
  • +
  • Replace all free occurrences of the formal by that argument
  • +
+

We say that (\x -> e1) e2 β-steps to e1[x := e2]

+













+

Redex Examples

+


+
((\x -> x) apple)     
+
+=b> apple
+

Is this right? Ask Elsa

+













+

QUIZ

+


+
((\x -> (\y -> y)) apple)
+
+=b> ???
+

A. apple

+

B. \y -> apple

+

C. \x -> apple

+

D. \y -> y

+

E. \x -> y

+


+












+

QUIZ

+


+
(\x -> (((y x) y) x)) apple
+=b> ???
+

A. (((apple apple) apple) apple)

+

B. (((y apple) y) apple)

+

C. (((y y) y) y)

+

D. apple

+












+

QUIZ

+


+
((\x -> (x (\x -> x))) apple)
+
+=b> ???
+

A. (apple (\x -> x))

+

B. (apple (\apple -> apple))

+

C. (apple (\x -> apple))

+

D. apple

+

E. (\x -> x)

+


+












+

EXERCISE

+

What is a λ-term fill_this_in such that

+


+
fill_this_in apple
+=b> banana
+

ELSA: https://goto.ucsd.edu/elsa/index.html

+

Click here to try this exercise

+












+

A Tricky One

+


+
((\x -> (\y -> x)) y)
+
+=b> \y -> y
+

Is this right?

+













+

Something is Fishy

+


+
(\x -> (\y -> x)) y
+
+=b> (\y -> y)
+

Is this right?

+

Problem: The free y in the argument has been captured by \y in body!

+


+

Solution: Ensure that formals in the body are different from free-variables of argument!

+












+

Capture-Avoiding Substitution

+

We have to fix our definition of β-reduction:

+
  (\x -> e1) e2   =b>   e1[x := e2]
+


where e1[x := e2] means e1 with all free occurrences of x replaced with e2

+
    +
  • e1 with all free occurrences of x replaced with e2
  • +
  • as long as no free variables of e2 get captured
  • +
+


+

Formally:

+
x[x := e]            = e
+
+y[x := e]            = y                 -- as x /= y
+
+(e1 e2)[x := e]      = (e1[x := e]) (e2[x := e])
+
+(\x -> e1)[x := e]   = (\x -> e1)        -- Q: Why leave `e1` unchanged?
+
+(\y -> e1)[x := e] 
+  | not (y in FV(e)) = \y -> e1[x := e]
+

Oops, but what to do if y is in the free-variables of e?

+
    +
  • i.e. if \y -> ... may capture those free variables?
  • +
+












+

Rewrite Rules of Lambda Calculus

+


+
    +
  1. β-step (aka function call)
  2. +
  3. α-step (aka renaming formals)
  4. +
+













+

Semantics: α-Renaming

+


+
  \x -> e   =a>   \y -> e[x := y]
+    where not (y in FV(e))
+


+
    +
  • We rename a formal parameter x to y

  • +
  • By replace all occurrences of x in the body with y

  • +
  • We say that \x -> e α-steps to \y -> e[x := y]

  • +
+



+

Example:

+
(\x -> x)   =a>   (\y -> y)   =a>    (\z -> z)
+

All these expressions are α-equivalent

+




+

What’s wrong with these?

+
-- (A)
+(\f -> (f x))    =a>   (\x -> (x x))
+
-- (B)
+((\x -> (\y -> y)) y)   =a>   ((\x -> (\z -> z)) z)
+














+

Tricky Example Revisited

+


+
    ((\x -> (\y -> x)) y)
+                                -- rename 'y' to 'z' to avoid capture
+    =a> ((\x -> (\z -> x)) y)
+                                -- now do b-step without capture!
+    =b> (\z -> y)
+



To avoid getting confused,

+
    +
  • you can always rename formals,

  • +
  • so different variables have different names!

  • +
+













+

Normal Forms

+

Recall redex is a λ-term of the form

+

((\x -> e1) e2)

+

A λ-term is in normal form if it contains no redexes.

+














+

QUIZ

+

Which of the following term are not in normal form ?

+

A. x

+

B. (x y)

+

C. ((\x -> x) y)

+

D. (x (\y -> y))

+

E. C and D

+













+

Semantics: Evaluation

+

A λ-term e evaluates to e' if

+
    +
  1. There is a sequence of steps
  2. +
+
e =?> e_1 =?> ... =?> e_N =?> e'
+

where each =?> is either =a> or =b> and N >= 0

+
    +
  1. e' is in normal form
  2. +
+







+

Examples of Evaluation

+
((\x -> x) apple)
+
+  =b> apple
+


+
(\f -> f (\x -> x)) (\x -> x)
+
+  =?> ???
+


+
(\x -> x x) (\x -> x)
+  =?> ???
+







+

Elsa shortcuts

+

Named λ-terms:

+
let ID = (\x -> x)  -- abbreviation for (\x -> x)
+



+

To substitute name with its definition, use a =d> step:

+
(ID apple)
+  =d> ((\x -> x) apple)  -- expand definition
+  =b> apple              -- beta-reduce
+



+

Evaluation:

+
    +
  • e1 =*> e2: e1 reduces to e2 in 0 or more steps +
      +
    • where each step is =a>, =b>, or =d>
    • +
  • +
  • e1 =~> e2: e1 evaluates to e2 and e2 is in normal form
  • +
+













+

EXERCISE

+

Fill in the definitions of FIRST, SECOND and THIRD such that you get the following behavior in elsa

+
let FIRST  = fill_this_in
+let SECOND = fill_this_in
+let THIRD  = fill_this_in
+
+eval ex1 : 
+  FIRST apple banana orange
+  =*> apple 
+
+eval ex2 : 
+  SECOND apple banana orange
+  =*> banana 
+  
+eval ex3 : 
+  THIRD apple banana orange
+  =*> orange
+

ELSA: https://goto.ucsd.edu/elsa/index.html

+

Click here to try this exercise

+

Non-Terminating Evaluation

+
((\x -> (x x)) (\x -> (x x)))
+
+  =b> ((\x -> (x x)) (\x -> (x x)))
+

Some programs loop back to themselves … never reduce to a normal form!

+

This combinator is called Ω

+







+

What if we pass Ω as an argument to another function?

+
let OMEGA = ((\x -> (x x)) (\x -> (x x)))
+
+((\x -> (\y -> y)) OMEGA)
+

Does this reduce to a normal form? Try it at home!

+










+

Programming in λ-calculus

+

Real languages have lots of features

+
    +
  • Booleans
  • +
  • Records (structs, tuples)
  • +
  • Numbers
  • +
  • Lists
  • +
  • Functions [we got those]
  • +
  • Recursion
  • +
+

Lets see how to encode all of these features with the λ-calculus.

+













+

Syntactic Sugar

+



+ + + + + + + + + + + + + + + + + + + + + +
instead ofwe write
\x -> (\y -> (\z -> e))\x -> \y -> \z -> e
\x -> \y -> \z -> e\x y z -> e
(((e1 e2) e3) e4)e1 e2 e3 e4
+



+
\x y -> y     -- A function that that takes two arguments
+              -- and returns the second one...
+              
+(\x y -> y) apple banana -- ... applied to two arguments
+













## λ-calculus: Booleans

+


+

How can we encode Boolean values (TRUE and FALSE) as functions?

+


+

Well, what do we do with a Boolean b?

+













+

Make a binary choice

+
    +
  • if b then e1 else e2
  • +
+




+

Booleans: API

+

We need to define three functions

+
let TRUE  = ???
+let FALSE = ???
+let ITE   = \b x y -> ???  -- if b then x else y
+

such that

+
ITE TRUE apple banana =~> apple
+ITE FALSE apple banana =~> banana
+

(Here, let NAME = e means NAME is an abbreviation for e)

+













+

Booleans: Implementation

+
let TRUE  = \x y -> x        -- Returns its first argument
+let FALSE = \x y -> y        -- Returns its second argument
+let ITE   = \b x y -> b x y  -- Applies condition to branches
+                             -- (redundant, but improves readability)
+













+

Example: Branches step-by-step

+
eval ite_true:
+  ITE TRUE e1 e2
+  =d> (\b x y -> b    x  y) TRUE e1 e2    -- expand def ITE  
+  =b>   (\x y -> TRUE x  y)      e1 e2    -- beta-step
+  =b>     (\y -> TRUE e1 y)         e2    -- beta-step
+  =b>            TRUE e1 e2               -- expand def TRUE
+  =d>     (\x y -> x) e1 e2               -- beta-step
+  =b>       (\y -> e1)   e2               -- beta-step
+  =b> e1
+







+

Example: Branches step-by-step

+

Now you try it!

+

Can you fill in the blanks to make it happen?

+
eval ite_false:
+  ITE FALSE e1 e2
+
+  -- fill the steps in!
+
+  =b> e2  
+













+

EXERCISE: Boolean Operators

+

ELSA: https://goto.ucsd.edu/elsa/index.html Click here to try this exercise

+

Now that we have ITE it’s easy to define other Boolean operators:

+
let NOT = \b     -> ???
+let OR  = \b1 b2 -> ???
+let AND = \b1 b2 -> ???
+

When you are done, you should get the following behavior:

+
eval ex_not_t:
+  NOT TRUE =*> FALSE
+  
+eval ex_not_f:
+  NOT FALSE =*> TRUE 
+  
+eval ex_or_ff:
+  OR FALSE FALSE =*> FALSE
+
+eval ex_or_ft:
+  OR FALSE TRUE =*> TRUE
+  
+eval ex_or_ft:
+  OR TRUE FALSE =*> TRUE
+
+eval ex_or_tt:
+  OR TRUE TRUE =*> TRUE
+  
+eval ex_and_ff:
+  AND FALSE FALSE =*> FALSE
+
+eval ex_and_ft:
+  AND FALSE TRUE =*> FALSE
+  
+eval ex_and_ft:
+  AND TRUE FALSE =*> FALSE
+
+eval ex_and_tt:
+  AND TRUE TRUE =*> TRUE
+












+

Programming in λ-calculus

+
    +
  • Booleans [done]
  • +
  • Records (structs, tuples)
  • +
  • Numbers
  • +
  • Lists
  • +
  • Functions [we got those]
  • +
  • Recursion
  • +
+













+

λ-calculus: Records

+

Let’s start with records with two fields (aka pairs)

+

What do we do with a pair?

+
    +
  1. Pack two items into a pair, then
  2. +
  3. Get first item, or
  4. +
  5. Get second item.
  6. +
+















+

Pairs : API

+

We need to define three functions

+
let PAIR = \x y -> ???    -- Make a pair with elements x and y 
+                          -- { fst : x, snd : y }
+let FST  = \p -> ???      -- Return first element 
+                          -- p.fst
+let SND  = \p -> ???      -- Return second element
+                          -- p.snd
+

such that

+
eval ex_fst: 
+  FST (PAIR apple banana) =*> apple
+
+eval ex_snd:
+  SND (PAIR apple banana) =*> banana
+













+

Pairs: Implementation

+

A pair of x and y is just something that lets you pick between x and y!

+
let PAIR = \x y -> (\b -> ITE b x y)
+

i.e. PAIR x y is a function that

+
    +
  • takes a boolean and returns either x or y
  • +
+

We can now implement FST and SND by “calling” the pair with TRUE or FALSE

+
let FST  = \p -> p TRUE   -- call w/ TRUE, get first value
+let SND  = \p -> p FALSE  -- call w/ FALSE, get second value
+













+

EXERCISE: Triples

+

How can we implement a record that contains three values?

+

ELSA: https://goto.ucsd.edu/elsa/index.html

+

Click here to try this exercise

+
let TRIPLE = \x y z -> ???
+let FST3   = \t -> ???
+let SND3   = \t -> ???
+let THD3   = \t -> ???
+
+eval ex1:
+  FST3 (TRIPLE apple banana orange)
+  =*> apple
+
+eval ex2:
+  SND3 (TRIPLE apple banana orange)
+  =*> banana 
+
+eval ex3:
+  THD3 (TRIPLE apple banana orange)
+  =*> orange
+













+

Programming in λ-calculus

+
    +
  • Booleans [done]
  • +
  • Records (structs, tuples) [done]
  • +
  • Numbers
  • +
  • Lists
  • +
  • Functions [we got those]
  • +
  • Recursion
  • +
+













+

λ-calculus: Numbers

+

Let’s start with natural numbers (0, 1, 2, …)

+

What do we do with natural numbers?

+
    +
  • Count: 0, inc
  • +
  • Arithmetic: dec, +, -, *
  • +
  • Comparisons: ==, <=, etc
  • +
+













+

Natural Numbers: API

+

We need to define:

+
    +
  • A family of numerals: ZERO, ONE, TWO, THREE, …
  • +
  • Arithmetic functions: INC, DEC, ADD, SUB, MULT
  • +
  • Comparisons: IS_ZERO, EQ
  • +
+

Such that they respect all regular laws of arithmetic, e.g.

+
IS_ZERO ZERO       =~> TRUE
+IS_ZERO (INC ZERO) =~> FALSE
+INC ONE            =~> TWO
+...
+













+

Natural Numbers: Implementation

+

Church numerals: a number N is encoded as a combinator that calls a function on an argument N times

+
let ONE   = \f x -> f x
+let TWO   = \f x -> f (f x)
+let THREE = \f x -> f (f (f x))
+let FOUR  = \f x -> f (f (f (f x)))
+let FIVE  = \f x -> f (f (f (f (f x))))
+let SIX   = \f x -> f (f (f (f (f (f x)))))
+...
+













+

QUIZ: Church Numerals

+

Which of these is a valid encoding of ZERO ?

+
    +
  • A: let ZERO = \f x -> x

  • +
  • B: let ZERO = \f x -> f

  • +
  • C: let ZERO = \f x -> f x

  • +
  • D: let ZERO = \x -> x

  • +
  • E: None of the above

  • +
+




+

Does this function look familiar?

+












+

λ-calculus: Increment

+
-- Call `f` on `x` one more time than `n` does
+let INC   = \n -> (\f x -> ???)
+













+

Example:

+
eval inc_zero :
+  INC ZERO
+  =d> (\n f x -> f (n f x)) ZERO
+  =b> \f x -> f (ZERO f x)
+  =*> \f x -> f x
+  =d> ONE
+













+

EXERCISE

+

Fill in the implementation of ADD so that you get the following behavior

+

Click here to try this exercise

+
let ZERO = \f x -> x
+let ONE  = \f x -> f x
+let TWO  = \f x -> f (f x)
+let INC  = \n f x -> f (n f x)
+
+let ADD  = fill_this_in 
+
+eval add_zero_zero: 
+  ADD ZERO ZERO =~> ZERO
+
+eval add_zero_one: 
+  ADD ZERO ONE =~> ONE
+
+eval add_zero_two: 
+  ADD ZERO TWO =~> TWO 
+
+eval add_one_zero: 
+  ADD ONE ZERO =~> ONE
+
+eval add_one_zero: 
+  ADD ONE ONE =~> TWO
+
+eval add_two_zero: 
+  ADD TWO ZERO =~> TWO  
+

QUIZ

+

How shall we implement ADD?

+

A. let ADD = \n m -> n INC m

+

B. let ADD = \n m -> INC n m

+

C. let ADD = \n m -> n m INC

+

D. let ADD = \n m -> n (m INC)

+

E. let ADD = \n m -> n (INC m)

+













+

λ-calculus: Addition

+
--  Call `f` on `x` exactly `n + m` times
+let ADD = \n m -> n INC m
+




+

Example:

+
eval add_one_zero :
+  ADD ONE ZERO
+  =~> ONE
+













+

QUIZ

+

How shall we implement MULT?

+

A. let MULT = \n m -> n ADD m

+

B. let MULT = \n m -> n (ADD m) ZERO

+

C. let MULT = \n m -> m (ADD n) ZERO

+

D. let MULT = \n m -> n (ADD m ZERO)

+

E. let MULT = \n m -> (n ADD m) ZERO

+













+

λ-calculus: Multiplication

+
--  Call `f` on `x` exactly `n * m` times
+let MULT = \n m -> n (ADD m) ZERO
+




+

Example:

+
eval two_times_three :
+  MULT TWO ONE
+  =~> TWO
+













+

Programming in λ-calculus

+
    +
  • Booleans [done]
  • +
  • Records (structs, tuples) [done]
  • +
  • Numbers [done]
  • +
  • Lists
  • +
  • Functions [we got those]
  • +
  • Recursion
  • +
+













+

λ-calculus: Lists

+

Lets define an API to build lists in the λ-calculus.

+

An Empty List

+
NIL
+

Constructing a list

+

A list with 4 elements

+
CONS apple (CONS banana (CONS cantaloupe (CONS dragon NIL)))
+

intuitively CONS h t creates a new list with

+
    +
  • head h
  • +
  • tail t
  • +
+

Destructing a list

+
    +
  • HEAD l returns the first element of the list
  • +
  • TAIL l returns the rest of the list
  • +
+
HEAD (CONS apple (CONS banana (CONS cantaloupe (CONS dragon NIL))))
+=~> apple
+
+TAIL (CONS apple (CONS banana (CONS cantaloupe (CONS dragon NIL))))
+=~> CONS banana (CONS cantaloupe (CONS dragon NIL)))
+













+

λ-calculus: Lists

+
let NIL  = ???
+let CONS = ???
+let HEAD = ???
+let TAIL = ???
+
+eval exHd:
+  HEAD (CONS apple (CONS banana (CONS cantaloupe (CONS dragon NIL))))
+  =~> apple
+
+eval exTl 
+  TAIL (CONS apple (CONS banana (CONS cantaloupe (CONS dragon NIL))))
+  =~> CONS banana (CONS cantaloupe (CONS dragon NIL)))
+













+

EXERCISE: Nth

+

Write an implementation of GetNth such that

+
    +
  • GetNth n l returns the n-th element of the list l
  • +
+

Assume that l has n or more elements

+
let GetNth = ???
+
+eval nth1 :
+  GetNth ZERO (CONS apple (CONS banana (CONS cantaloupe NIL)))
+  =~> apple 
+
+eval nth1 :
+  GetNth ONE (CONS apple (CONS banana (CONS cantaloupe NIL)))
+  =~> banana
+
+eval nth2 :
+  GetNth TWO (CONS apple (CONS banana (CONS cantaloupe NIL)))
+  =~> cantaloupe
+

Click here to try this in elsa

+









+

λ-calculus: Recursion

+


+

I want to write a function that sums up natural numbers up to n:

+
let SUM = \n -> ...  -- 0 + 1 + 2 + ... + n
+

such that we get the following behavior

+
eval exSum0: SUM ZERO  =~> ZERO
+eval exSum1: SUM ONE   =~> ONE
+eval exSum2: SUM TWO   =~> THREE
+eval exSum3: SUM THREE =~> SIX
+

Can we write sum using Church Numerals?

+

Click here to try this in Elsa

+











+

QUIZ

+

You can write SUM using numerals but its tedious.

+

Is this a correct implementation of SUM?

+
let SUM = \n -> ITE (ISZ n) 
+            ZERO 
+            (ADD n (SUM (DEC n)))
+

A. Yes

+

B. No

+











+

No!

+
    +
  • Named terms in Elsa are just syntactic sugar
  • +
  • To translate an Elsa term to λ-calculus: replace each name with its definition
  • +
+
\n -> ITE (ISZ n) 
+        ZERO 
+        (ADD n (SUM (DEC n))) -- But SUM is not yet defined!
+



+

Recursion:

+
    +
  • Inside this function
  • +
  • Want to call the same function on DEC n
  • +
+



+

Looks like we can’t do recursion!

+
    +
  • Requires being able to refer to functions by name,
  • +
  • But λ-calculus functions are anonymous.
  • +
+

Right?

+













+

λ-calculus: Recursion

+

Think again!

+



+

Recursion:

+

Instead of

+
    +
  • Inside this function I want to call the same function on DEC n
  • +
+

Lets try

+
    +
  • Inside this function I want to call some function rec on DEC n
  • +
  • And BTW, I want rec to be the same function
  • +
+



+

Step 1: Pass in the function to call “recursively”

+
let STEP = 
+  \rec -> \n -> ITE (ISZ n) 
+                  ZERO 
+                  (ADD n (rec (DEC n))) -- Call some rec
+



+

Step 2: Do some magic to STEP, so rec is itself

+
\n -> ITE (ISZ n) ZERO (ADD n (rec (DEC n)))
+

That is, obtain a term MAGIC such that

+
MAGIC =*> STEP MAGIC 
+













+

λ-calculus: Fixpoint Combinator

+

Wanted: a λ-term FIX such that

+
    +
  • FIX STEP calls STEP with FIX STEP as the first argument:
  • +
+
(FIX STEP) =*> STEP (FIX STEP)
+


+

(In math: a fixpoint of a function f(x) is a point x, such that f(x) = x)

+





+

Once we have it, we can define:

+
let SUM = FIX STEP
+

Then by property of FIX we have:

+
SUM   =*>   FIX STEP  =*>   STEP (FIX STEP)   =*>   STEP SUM
+

and so now we compute:

+
eval sum_two:
+  SUM TWO
+  =*> STEP SUM TWO
+  =*> ITE (ISZ TWO) ZERO (ADD TWO (SUM (DEC TWO)))
+  =*> ADD TWO (SUM (DEC TWO))
+  =*> ADD TWO (SUM ONE)
+  =*> ADD TWO (STEP SUM ONE)
+  =*> ADD TWO (ITE (ISZ ONE) ZERO (ADD ONE (SUM (DEC ONE))))
+  =*> ADD TWO (ADD ONE (SUM (DEC ONE)))
+  =*> ADD TWO (ADD ONE (SUM ZERO))
+  =*> ADD TWO (ADD ONE (ITE (ISZ ZERO) ZERO (ADD ZERO (SUM DEC ZERO)))
+  =*> ADD TWO (ADD ONE (ZERO)) 
+  =*> THREE
+

How should we define FIX???

+













+

The Y combinator

+

Remember Ω?

+
(\x -> x x) (\x -> x x)
+=b> (\x -> x x) (\x -> x x)
+

This is self-replcating code! We need something like this but a bit more involved…

+





+

The Y combinator discovered by Haskell Curry:

+
let FIX   = \stp -> (\x -> stp (x x)) (\x -> stp (x x))
+



+

How does it work?

+
eval fix_step:
+  FIX STEP
+  =d> (\stp -> (\x -> stp (x x)) (\x -> stp (x x))) STEP
+  =b> (\x -> STEP (x x)) (\x -> STEP (x x))
+  =b> STEP ((\x -> STEP (x x)) (\x -> STEP (x x)))
+  --       ^^^^^^^^^^ this is FIX STEP ^^^^^^^^^^^
+






+

That’s all folks, Haskell Curry was very clever.

+

Next week: We’ll look at the language named after him (Haskell)

+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/lectures/02-haskell-old.html b/docs/lectures/02-haskell-old.html new file mode 100644 index 0000000..e91d56d --- /dev/null +++ b/docs/lectures/02-haskell-old.html @@ -0,0 +1,1026 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

A crash course in Haskell

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

Functions and Programming

+



+

+

+






+

What is Haskell?

+


+

A typed, lazy, purely functional programming language

+


+

Haskell = λ-calculus ++

+
    +
  • better syntax
  • +
  • types
  • +
  • built-in features +
      +
    • booleans, numbers, characters
    • +
    • records (tuples)
    • +
    • lists
    • +
    • recursion
    • +
    • +
  • +
+







+

Why Haskell?

+

Haskell programs tend to be simple and correct

+

QuickSort in Haskell

+
sort        :: (Ord a) => [a] -> [a]
+sort []     = []
+sort (x:xs) = sort ls ++ [x] ++ sort rs
+  where
+    ls      = [ l | l <- xs, l <= x ]
+    rs      = [ r | r <- xs, x <  r ]
+

Goals for this week

+
    +
  1. Understand the code above
  2. +
  3. Understand what typed, lazy, and purely functional means (and why it’s cool)
  4. +
+







+

Haskell vs λ-calculus: similarities

+

(1) Programs

+

A program is an expression (not a sequence of statements)

+

It evaluates to a value (it does not perform actions)

+
    +
  • λ:

    +
    (\x -> x) apple     -- =~> apple
  • +
  • Haskell:

    +
    (\x -> x) "apple"   -- =~> "apple"
  • +
+

(2) Functions

+

Functions are first-class values:

+
    +
  • can be passed as arguments to other functions
  • +
  • can be returned as results from other functions
  • +
  • can be partially applied (arguments passed one at a time)
  • +
+
(\x -> (\y -> x (x y))) (\z -> z + 1) 0   -- =~> ???
+

But: unlike λ-calculus, not everything is a function!

+

(3) Top-level bindings

+

Like in Elsa, we can name terms to use them later

+

Elsa:

+
let T    = \x y -> x
+let F    = \x y -> y
+
+let PAIR = \x y -> \b -> ITE b x y
+let FST  = \p -> p T
+let SND  = \p -> p F
+
+eval fst:
+ FST (PAIR apple orange)
+ =~> apple
+

Haskell:

+
haskellIsAwesome = True
+
+pair = \x y -> \b -> if b then x else y
+fst = \p -> p haskellIsAwesome
+snd = \p -> p False
+
+-- In GHCi:
+> fst (pair "apple" "orange")   -- "apple"
+

The names are called top-level variables

+

Their definitions are called top-level bindings

+







+

Better Syntax: Equations and Patterns

+

You can define function bindings using equations:

+
pair x y b = if b then x else y -- same as: pair = \x y b -> ...
+fst p      = p True             -- same as: fst = \p -> ...
+snd p      = p False            -- same as: snd = \p -> ...
+







+

A single function binding can have multiple equations with different patterns of parameters:

+
pair x y True  = x  -- If 3rd arg matches True,
+                    -- use this equation;
+pair x y False = y  -- Otherwise, if 3rd arg matches False,
+                    -- use this equation.
+

At run time, the first equation whose pattern matches the actual arguments is chosen

+

For now, a pattern is:

+
    +
  • a variable (matches any value)

  • +
  • or a value (matches only that value)

  • +
+



+

Same as:

+
pair x y True  = x  -- If 3rd arg matches True,
+                    -- use this equation;
+pair x y b     = y  -- Otherwise, use this equation.
+



+

Same as:

+
pair x y True  = x
+pair x y _     = y
+







+

QUIZ

+

Which of the following definitions of pair is incorrect?

+

A. pair x y = \b -> if b then x else y

+

B. pair x = \y b -> if b then x else y

+

C.

+
pair x _ True  = x
+pair _ y _     = y
+

D.

+
pair x y b     = x
+pair x y False = y
+

E. all of the above

+


+






+

Equations with guards

+

An equation can have multiple guards (Boolean expressions):

+
cmpSquare x y  |  x > y*y   =  "bigger :)"
+               |  x == y*y  =  "same :|"
+               |  x < y*y   =  "smaller :("
+

Same as:

+
cmpSquare x y  |  x > y*y   =  "bigger :)"
+               |  x == y*y  =  "same :|"
+               |  otherwise =  "smaller :("
+










+

Recusion

+

Recursion is built-in, so you can write:

+
sum n = if n == 0 
+          then 0 
+          else n + sum (n - 1)
+

or you can write:

+
sum 0 = 0
+sum n = n + sum (n - 1)
+









+

The scope of variables

+

Top-level variable have global scope, so you can write:

+
message = if haskellIsAwesome          -- this var defined below
+            then "I love CSE 130"
+            else "I'm dropping CSE 130"
+            
+haskellIsAwesome = True
+



+

Or you can write:

+
-- What does f compute?
+f 0 = True
+f n = g (n - 1) -- mutual recursion!
+
+g 0 = False
+g n = f (n - 1) -- mutual recursion!
+




+

Is this allowed?

+
haskellIsAwesome = True
+
+haskellIsAwesome = False -- changed my mind
+





+

Local variables

+

You can introduce a new (local) scope using a let-expression:

+
sum 0 = 0
+sum n = let n' = n - 1          
+        in n + sum n'  -- the scope of n' is the term after in
+




+

Syntactic sugar for nested let-expressions:

+
sum 0 = 0
+sum n = let 
+          n'   = n - 1
+          sum' = sum n'
+        in n + sum'
+




+

If you need a variable whose scope is an equation, use the where clause instead:

+
cmpSquare x y  |  x > z   =  "bigger :)"
+               |  x == z  =  "same :|"
+               |  x < z   =  "smaller :("
+  where z = y*y
+









+

Types

+





+

What would Elsa say?

+
let WEIRDO = ONE ZERO
+


+




+

What would Python say?

+
def weirdo():
+  return 0(1)
+


+




+

What would Java say?

+
void weirdo() {
+  int zero;
+  zero(1);
+}
+


+







+

Types

+

+

In Haskell every expression either

+
    +
  • ill-typed and rejected at compile time or
  • +
  • has a type and can be evaluated to obtain _ a value of the same type.
  • +
+

Ill-typed* expressions are rejected statically at compile-time, before execution starts

+
    +
  • like in Java
  • +
  • unlike λ-calculus or Python …
  • +
+
weirdo = 1 0     -- rejected by GHC
+









+

Why are types good?

+
    +
  • Helps with program design
  • +
  • Types are contracts (ignore ill-typed inputs!)
  • +
  • Catches errors early
  • +
  • Allows compiler to generate code
  • +
  • Enables compiler optimizations
  • +
+







+

Type annotations

+

You can annotate your bindings with their types using ::, like so:

+
-- | This is a Boolean:
+haskellIsAwesome :: Bool            
+haskellIsAwesome = True
+
+-- | This is a string
+message :: String
+message = if haskellIsAwesome
+            then "I love CSE 130"
+            else "I'm dropping CSE 130"
+            
+-- | This is a word-size integer
+rating :: Int
+rating = if haskellIsAwesome then 10 else 0
+
+-- | This is an arbitrary precision integer
+bigNumber :: Integer
+bigNumber = factorial 100
+

If you omit annotations, GHC will infer them for you

+
    +
  • Inspect types in GHCi using :t
  • +
  • You should annotate all top-level bindings anyway! (Why?)
  • +
+







+

Function Types

+

Functions have arrow types:

+
    +
  • \x -> e has type A -> B
  • +
  • if e has type B assuming x has type A
  • +
+

For example:

+
> :t (\x -> if x then `a` else `b`)  -- ???
+





+

Always annotate your function bindings

+

First understand what the function does

+
    +
  • Before you think about how to do it
  • +
+
sum :: Int -> Int
+sum 0 = 0
+sum n = n + sum (n - 1)
+





+

When you have *multiple arguments

+

For example

+
add3 :: Int -> (Int -> (Int -> Int))
+add3 x y z = x + y + z
+

why? because the above is the same as:

+
add3 :: Int -> (Int -> (Int -> Int))
+add3 = \x -> (\y -> (\z -> x + y + z))
+

however, as with the lambdas, the -> associates to the right so we will just write:

+
add3 :: Int -> Int -> Int -> Int
+add3 x y z = x + y + z
+







+ +

Lists

+

A list is

+
    +
  • either an empty list

    +

    [] -- pronounced "nil"

  • +
  • or a head element attached to a tail list

    +

    h : t -- pronounced "h cons t"

  • +
+



+

Examples:

+
[]                -- A list with zero elements
+
+1 : []            -- A list with one element: 1
+
+(:) 1 []          -- As above: for any infix op, `x op y` is same as `(op) x y`
+
+1:(2:(3:(4:[])))  -- A list with four elements: 1, 2, 3, 4
+
+1:2:3:4:[]        -- Same thing (: is right associative)
+
+[1,2,3,4]         -- Same thing (syntactic sugar)
+





+

Terminology: constructors and values

+

[] and (:) are called the list constructors

+

We’ve seen constructors before:

+
    +
  • True and False are Bool constructors
  • +
  • 0, 1, 2 are … well, you can think of them as Int constructors +
      +
    • The Int constructors don’t take any parameters, we just called them values
    • +
  • +
+

In general, a value is a constructor applied to other values

+
    +
  • examples above are list values
  • +
+









+

The Type of a List

+

A list has type [Thing] if each of its elements has type Thing

+

Examples:

+
intList :: [Int]
+intList = [1,2,3,4]
+
+boolList :: [Bool]
+boolList = [True, False, True]
+
+strList :: [String]
+strList = ["nom", "nom", "burp"]
+







+ +

Lets write some Functions

+

A Recipe

+

Step 1: Write some tests

+

Step 2: Write the type

+

Step 3: Write the code

+

Functions on lists: range

+

1. Tests

+
-- >>> ???
+

2. Type

+
range :: ???
+

3. Code

+
range = ???
+ +






+

Syntactic Sugar for Ranges

+

There’s also syntactic sugar for this!

+
[1..7]    -- [1,2,3,4,5,6,7]
+[1,3..7]  -- [1,3,5,7]
+









+

Functions on lists: length

+

1. Tests

+
-- >>> ???
+

2. Type

+
len :: ???
+

3. Code

+
len = ???
+











+

Pattern matching on lists

+
-- | Length of the list
+len :: [Int] -> Int
+len []     = 0
+len (_:xs) = 1 + len xs
+



+

A pattern is either a variable (incl. _) or a value

+

A pattern is

+
    +
  • either a variable (incl. _)
  • +
  • or a constructor applied to other patterns
  • +
+



+

Pattern matching attempts to match values against patterns and, if desired, bind variables to successful matches.

+







+

Functions on lists: take

+

Let’s write a function to take first n elements of a list xs.

+

1. Tests

+
-- >>> ???
+

2. Type

+
take :: ???
+

3. Code

+
take = ???
+

QUIZ

+

Which of the following is not a pattern?

+

A. (1:xs)

+

B. (_:_:_)

+

C. [x]

+

D. [1+2,x,y]

+

E. all of the above

+


+






+

Strings are Lists-of-Chars

+

For example

+
λ> let x = ['h', 'e', 'l', 'l', 'o']
+λ> x
+"hello"
+
+λ> let y = "hello"
+
+λ> x == y
+True
+
+λ> :t x
+x :: [Char]
+
+λ> :t y
+y :: [Char]
+

shout Shout SHOUT

+

How can we convert a string to upper-case, e.g.

+
ghci> shout "like this"
+"LIKE THIS"
+
shout :: String -> String
+shout s = ???
+


+

Some useful library functions

+
-- | Length of the list
+length :: [t] -> Int
+
+-- | Append two lists
+(++) :: [t] -> [t] -> [t]
+
+-- | Are two lists equal?
+(==) :: [t] -> [t] -> Bool
+


+

You can search for library functions on Hoogle!

+







+

Tuples

+
myPair :: (String, Int)  -- pair of String and Int
+myPair = ("apple", 3)
+


+

(,) is the pair constructor

+



+

Field access

+

Using fst and snd

+
ghci> fst ("apple", 22)
+"apple"
+
+ghci> snd ("apple", 22)
+22
+

Tuples to pass parameters

+
add2 :: (Int, Int) -> Int
+add2 p = fst p + snd p
+

but watch out, add2 expects a tuple.

+
exAdd2_BAD = add2 10 20      -- type error
+
+exAdd2_OK  = add2 (10, 20)   -- OK!
+

Tuples and Pattern Matching

+

It is often clearer to use patterns for tuples, e.g.

+
add2 :: (Int, Int) -> Int
+add2 p = let (x, y) = p in
+           x + y
+

or equivalently,

+
add2 :: (Int, Int) -> Int
+add2 p    = x + y
+  where
+   (x, y) = p
+

or, best, use the pattern in the parameter,

+
add2 :: (Int, Int) -> Int
+add2 (x, y) = x + y
+



+

You can use pattern matching not only in equations, but also in λ-bindings and let-bindings!

+






+

QUIZ: Pattern matching with pairs

+

Is this pattern matching correct? What does this function do?

+
quiz :: String -> [(String, Int)] -> Int
+quiz _ []     = 0
+quiz x ((k,v) : ps)
+  | x == k    = v
+  | otherwise = quiz x ps
+

What is quiz "dog" [ ("cat", 10), ("dog", 20), ("cat", 30)] ?

+

A. Type error!

+

B. 0

+

C. 10

+

D. 20

+

D. 30

+



+






+

Generalized Tuples

+

Can we implement triples like in λ-calculus?

+





+

Sure! but Haskell has native support for n-tuples:

+
myPair   :: (String, Int)
+myPair   = ("apple", 3)
+
+myTriple :: (Bool, Int, [Int])
+myTriple = (True, 1, [1,2,3])
+
+my4tuple :: (Float, Float, Float, Float)
+my4tuple = (pi, sin pi, cos pi, sqrt 2)
+

The “Empty” Tuple

+

It also makes sense to have an 0-ary tuple:

+
myUnit :: ()
+myUnit = ()
+

often used like void in other languages.

+







+

List comprehensions

+

A convenient way to construct lists!

+

QUIZ

+

What is the result of evaluating:

+
quiz = [ 10 * i | i <- [0,1,2,3,4,5]]
+

A. Infinite loop B. [] C. [0, 10, 20, 30, 40, 50] D. 150 E. Type error

+






+

Comprehensions and Ranges

+

Recall you can enumerate ranges as

+
ghci> [0..5]
+[0,1,2,3,4,5]
+

So, we can write the above more simply

+
quiz = [ 10 * i | i <- [0..5] ]
+

QUIZ: Composing Comprehensions

+

What is the result of evaluating

+
quiz = [(i,j) | i <- [0, 1]     -- a first selection
+              , j <- [0, 1] ]   -- a second selection
+

A. Type error B. [] C. [0,1] D. [(0,0), (1,1)] E. [(0,0), (0,1, (1,0), (1,1)]

+






+

QUIZ: Composing Comprehensions

+

What is the result of evaluating

+
quiz = [(i,j) | i <- [0, 1]
+              , j <- [0, 1]
+              , i == j      ]   -- condition!
+

A. Type error B. [] C. [0,1] D. [(0,0), (1,1)] E. [(0,0), (0,1, (1,0), (1,1)]

+







+

shout revisited

+

How can we convert a string to upper-case, e.g.

+
ghci> shout "like this"
+"LIKE THIS"
+

Use comprehensions to write a *non-recursive" shout?

+
shout :: String -> String
+shout s = ???
+






+

QuickSort in Haskell

+

Step 1: Write some tests

+
-- >>> sort []
+-- ???
+
+-- >>> sort [10]
+-- ???
+
+-- >>> sort [12, 1, 10]
+-- ???
+

Step 2: Write the type

+
sort :: ???
+

Step 3: Write the code

+
sort []     = ???
+sort (x:xs) = ???
+
sort :: [Int] -> [Int]
+sort []     = []
+sort (x:xs) = sort ls ++ [x] ++ sort rs
+  where
+    ls      = [ l | l <- xs, l <= x ]
+    rs      = [ r | r <- xs, x <  r ]
+







+

Haskell is purely functional

+

Functional = functions are first-class values

+

Pure = a program is an expression that evaluates to a value

+
    +
  • no side effects!

  • +
  • unlike in Python, Java, etc:

    +
    public int f(int x) {
    +  calls++;                         // side effect: global variable update!
    +  System.out.println("calling f"); // side effect: writing to screen!
    +  launchMissile();                 // side effect: can't bring back home!
    +  return x * 2;
    +}
  • +
  • in Haskell, a function of type Int -> Int Computes a single integer output from a single integer input Does nothing else

  • +
+

Referential transparency: The same expression always evaluates to the same value

+ +


+

Why is this good?

+
    +
  • easier to reason about (remember x++ vs ++x in C++?)
  • +
  • enables compiler optimizations
  • +
  • especially great for parallelization (e1 + e2: we can always compute e1 and e2 in parallel!)
  • +
+







+

QUIZ

+

The function head returns the first element of a list.

+

What is the result of:

+
goBabyGo :: Int -> [Int]
+goBabyGo n = n : goBabyGo (n + 1)
+
+quiz :: Int
+quiz = head (goBabyGo 0)
+

A. Loops forever B. Type error C. 0 D. 1

+

Haskell is Lazy

+

An expression is evaluated only when its result is needed!

+
ghci> take 2 (goBabyGo 1)
+[1,2]
+

Why?

+



+
        take 2 (goBabyGo 1)
+=>      take 2 (1 : goBabyGo 2)
+=>      take 2 (1 : 2 : goBabyGo 3)
+=> 1:   take 1 (    2 : goBabyGo 3)
+=> 1:2: take 0 (        goBabyGo 3)
+=> 1:2: []
+


+

Why is this good?

+
    +
  • can implement cool stuff like infinite lists: [1..]

    +
    -- first n pairs of co-primes: 
    +take n [(i,j) | i <- [1..],
    +                j <- [1..i],
    +                gcd i j == 1]
  • +
  • encourages simple, general solutions

  • +
  • but has its problems too :(

  • +
+






+

That’s all folks!

+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/lectures/02-haskell-rec.html b/docs/lectures/02-haskell-rec.html new file mode 100644 index 0000000..eecb7b7 --- /dev/null +++ b/docs/lectures/02-haskell-rec.html @@ -0,0 +1,785 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

Haskell Crash Course Part I

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

From the Lambda Calculus to Haskell

+














+

What is Haskell?

+


+

A typed, lazy, purely functional programming language

+


+

Haskell = λ-calculus ++

+
    +
  • better syntax
  • +
  • types
  • +
  • built-in features +
      +
    • booleans, numbers, characters
    • +
    • records (tuples)
    • +
    • lists
    • +
    • recursion
    • +
    • +
  • +
+







+

Programming in Haskell

+

Computation by Calculation

+


+

Substituting equals by equals

+













+

Computation via Substituting Equals by Equals

+

+    (1 + 3) * (4 + 5)
+                        -- subst 1 + 3 = 4
+==>       4 * (4 + 5)
+                        -- subst 4 + 5 = 9
+==>       4 * 9
+                        -- subst 4 * 9 = 36
+==>       36
+









+

Computation via Substituting Equals by Equals

+

Equality-Substitution enables Abstraction via Pattern Recognition

+











+

Abstraction via Pattern Recognition

+

Repeated Expressions

+
                31 * (42 + 56)
+                70 * (12 + 95)
+                90 * (68 + 12)
+

Recognize Pattern as λ-function

+
pat = \x y z -> x  * ( y + z ) 
+

Equivalent Haskell Definition

+
pat   x y z =  x  * ( y + z ) 
+

Function Call is Pattern Instance

+
pat 31 42 56 =*> 31 * (42 + 56) =*> 31 * 98  =*> 3038
+pat 70 12 95 =*> 70 * (12 + 95) =*> 70 * 107 =*> 7490
+pat 90 68 12 =*> 90 * (68 + 12) =*> 90 * 80  =*> 7200
+

Key Idea: Computation is substitute equals by equals.

+











+

Programming in Haskell

+

Substitute Equals by Equals

+



+

Thats it! (Do not think of registers, stacks, frames etc.)

+








+

Elements of Haskell

+

+
    +
  • Core program element is an expression
  • +
  • Every valid expression has a type (determined at compile-time)
  • +
  • Every valid expression reduces to a value (computed at run-time)
  • +
+

Ill-typed* expressions are rejected at compile-time before execution

+
    +
  • like in Java
  • +
  • not like λ-calculus or Python …
  • +
+
weirdo = 1 0     -- rejected by GHC
+









+

Why are types good?

+
    +
  • Helps with program design
  • +
  • Types are contracts (ignore ill-typed inputs!)
  • +
  • Catches errors early
  • +
  • Allows compiler to generate code
  • +
  • Enables compiler optimizations
  • +
+











+

The Haskell Eco-System

+
    +
  • Batch compiler: ghc Compile and run large programs

  • +
  • Interactive Shell ghci Shell to interactively run small programs online

  • +
  • Build Tool stack Build tool to manage libraries etc.

  • +
+









+

Interactive Shell: ghci

+
$ stack ghci
+
+:load file.hs
+:type expression 
+:info variable
+









+

A Haskell Source File

+

A sequence of top-level definitions x1, x2, …

+
    +
  • Each has type type_1, type_2, …

  • +
  • Each defined by expression expr_1, expr_2, …

  • +
+
x_1 :: type_1
+x_1 = expr_1
+
+x_2 :: type_2
+x_2 = expr_2
+
+.
+.
+.
+









+

Basic Types

+
ex1 :: Int
+ex1 = 31 * (42 + 56)   -- this is a comment
+
+ex2 :: Double
+ex2 = 3 * (4.2 + 5.6)  -- arithmetic operators "overloaded"
+
+ex3 :: Char 
+ex3 = 'a'              -- 'a', 'b', 'c', etc. built-in `Char` values
+
+ex4 :: Bool 
+ex4 = True             -- True, False are builtin Bool values
+
+ex5 :: Bool 
+ex5 = False
+









+

QUIZ: Basic Operations

+
ex6 :: Int
+ex6 = 4 + 5
+
+ex7 :: Int
+ex7 = 4 * 5
+
+ex8 :: Bool
+ex8 = 5 > 4 
+
+quiz :: ???
+quiz = if ex8 then ex6 else ex7
+

What is the type of quiz?

+

A. Int

+

B. Bool

+

C. Error!

+









+

QUIZ: Basic Operations

+
ex6 :: Int
+ex6 = 4 + 5
+
+ex7 :: Int
+ex7 = 4 * 5
+
+ex8 :: Bool
+ex8 = 5 > 4 
+
+quiz :: ???
+quiz = if ex8 then ex6 else ex7
+

What is the value of quiz?

+

A. 9

+

B. 20

+

C. Other!

+









+

Function Types

+

In Haskell, a function is a value that has a type

+
A -> B
+

A function that

+
    +
  • takes input of type A
  • +
  • returns output of type B
  • +
+

For example

+
isPos :: Int -> Bool
+isPos = \n -> (x > 0)
+

Define function-expressions using \ like in λ-calculus!

+

But Haskell also allows us to put the parameter on the left

+
isPos :: Int -> Bool
+isPos n = (x > 0)
+

(Meaning is identical to above definition with \n -> ...)

+









+

Multiple Argument Functions

+

A function that

+
    +
  • takes three inputs A1, A2 and A3
  • +
  • returns one output B has the type
  • +
+
A1 -> A2 -> A3 -> B
+

For example

+
pat :: Int -> Int -> Int -> Int
+pat = \x y z -> x * (y + z)
+

which we can write with the params on the left as

+
pat :: Int -> Int -> Int -> Int
+pat x y z = x * (y + z)
+









+

QUIZ

+

What is the type of quiz ?

+
quiz :: ???
+quiz x y = (x + y) > 0
+

A. Int -> Int

+

B. Int -> Bool

+

C. Int -> Int -> Int

+

D. Int -> Int -> Bool

+

E. (Int, Int) -> Bool

+









+

Function Calls

+

A function call is exactly like in the λ-calculus

+
e1 e2
+

where e1 is a function and e2 is the argument. For example

+
>>> isPos 12
+True 
+
+>>> isPos (0 - 5)
+False
+









+

Multiple Argument Calls

+

With multiple arguments, just pass them in one by one, e.g.

+
(((e e1) e2) e3)
+

For example

+
>>> pat 31 42 56 
+3038
+









+

EXERCISE

+

Write a function myMax that returns the maximum of two inputs

+
myMax :: Int -> Int -> Int
+myMax = ???
+

When you are done you should see the following behavior:

+
>>> myMax 10 20
+20
+
+>>> myMax 100 5
+100
+









+

EXERCISE

+

Write a function sumTo such that sumTo n evaluates to 0 + 1 + 2 + ... + n

+
sumTo :: Int -> Int
+sumTo n = ???
+

When you are done you should see the following behavior:

+
>>> sumTo 3
+6 
+>>> sumTo 4
+10 
+>>> sumTo 5
+15
+









+

How to Return Multiple Outputs?

+









+

Tuples

+

A type for packing n different kinds of values into a single “struct”

+
(T1,..., Tn)
+

For example

+
tup1 :: ???
+tup1 = ('a', 5) 
+
+tup2 :: (Char, Double, Int)
+tup2 = ('a', 5.2, 7) 
+









+

QUIZ

+

What is the type ??? of tup3?

+
tup3 :: ???
+tup3 = ((7, 5.2), True)
+

A. (Int, Bool)

+

B. (Int, Double, Bool)

+

C. (Int, (Double, Bool))

+

D. ((Int, Double), Bool)

+

E. (Tuple, Bool)

+









+

Extracting Values from Tuples

+

We can create a tuple of three values e1, e2, and e3

+
tup = (e1, e2, e3)
+

… but how to extract the values from this tuple?

+

Pattern Matching via case-of expressions

+
fst3 :: (t1, t2, t3) -> t1
+fst3 t = case t of 
+           (x1, x2, x3) -> x1
+
+snd3 :: (t1, t2, t3) -> t2
+snd3 t = case t of 
+           (x1, x2, x3) -> x2
+
+thd3 :: (t1, t2, t3) -> t3
+thd3 t = case t of 
+           (x1, x2, x3) -> x3
+









+

QUIZ

+

What is the value of quiz defined as

+
tup2 :: (Char, Double, Int)
+tup2 = ('a', 5.2, 7) 
+
+snd3 :: (t1, t2, t3) -> t2
+snd3 t = case t of 
+           (x1, x2, x3) -> x2
+
+quiz = snd3 tup2
+

A. 'a'

+

B. 5.2

+

C. 7

+

D. ('a', 5.2)

+

E. (5.2, 7)

+









+

Lists

+

Unbounded Sequence of values of type T

+
[T]
+

For example

+
chars :: [Char]
+chars = ['a', 'b', 'c'] 
+
+ints :: [Int]
+ints = [1, 3, 5, 7] 
+
+pairs :: [(Int, Bool)]
+pairs = [(1,True), (2,False)]
+









+

QUIZ

+

What is the type of things defined as

+
things :: ???
+things = [ [1], [2, 3], [4, 5, 6] ] 
+

A. [Int]

+

B. ([Int], [Int], [Int])

+

C. [(Int, Int, Int)]

+

D. [[Int]]

+

E. List

+









+

List’s Values Must Have The SAME Type!

+

The type [T] denotes an unbounded sequence of values of type T

+

Suppose you have a list

+
oops = [1, 2, 'c']
+

There is no T that we can use

+
    +
  • As last element is not Int
  • +
  • First two elements are not Char!
  • +
+

Result: Mysterious Type Error!

+









+

Constructing Lists

+

There are two ways to construct lists

+
    []     -- creates an empty list 
+    h:t    -- creates a list with "head" 'h' and "tail" t 
+

For example

+
>>> 3 : []
+[3]
+
+>>> 2 : (3 : []) 
+[2, 3]
+
+>>> 1 : (2 : (3 : [])) 
+[1, 2, 3]
+

Cons Operator : is Right Associative

+

x1 : x2 : x3 : x4 : t means x1 : (x2 : (x3 : (x4 : t)))

+

So we can just avoid the parentheses.

+

Syntactic Sugar

+

Haskell lets you write [x1, x2, x3, x4] instead of x1 : x2 : x3 : x4 : []

+









+

Functions Producing Lists

+

Lets write a function copy3 that

+
    +
  • takes an input x and
  • +
  • returns a list with three copies of x
  • +
+
copy3 :: ???
+copy3 x = ???
+

When you are done, you should see the following

+
>>> copy3 5
+[5, 5, 5]
+
+>>> copy3 "cat"
+["cat", "cat", "cat"]
+









+

PRACTICE: Clone

+

Write a function clone such that clone n x returns a list with n copies of x.

+
clone :: ???
+clone n x = ???
+

When you are done you should see the following behavior

+
>>> clone 0 "cat" 
+[]
+
+>>> clone 1 "cat" 
+["cat"]
+
+>>> clone 2 "cat" 
+["cat", "cat"]
+
+>>> clone 3 "cat" 
+["cat", "cat", "cat"]
+
+>>> clone 3 100
+[100, 100, 100]
+















+

How does clone execute?

+

(Substituting equals-by-equals!)

+
clone 3 100
+  =*> ???
+














+

EXERCISE: Range

+

Write a function range such that range i j returns the list of values [i, i+1, ..., j]

+
range :: ???
+range i j = ???
+

When we are done you should get the behavior

+
>>> range 4 3 
+[]
+
+>>> range 3 3 
+[3]
+
+>>> range 2 3 
+[2, 3]
+
+>>> range 1 3 
+[1, 2, 3]
+
+>>> range 0 3 
+[0, 1, 2, 3]
+









+

Functions Consuming Lists

+

So far: how to produce lists.

+

Next how to consume lists!

+








+









+

EXERCISE

+

Lets write a function firstElem such that firstElem xs returns the first element xs if it is a non-empty list, and 0 otherwise.

+
firstElem :: [Int] -> Int
+firstElem xs = ???
+

HINT: How to extract values from a list?

+

When you are done you should see the following behavior:

+
>>> firstElem []
+0
+
+>>> firstElem [10, 20, 30]
+10
+
+>>> firstElem [5, 6, 7, 8] 
+5
+









+

QUIZ

+

Suppose we have the following mystery function

+
mystery :: [a] -> Int
+mystery l = case l of 
+              []     -> 0
+              (x:xs) -> 1 + mystery xs
+

What does mystery [10, 20, 30] evaluate to?

+

A. 10

+

B. 20

+

C. 30

+

D. 3

+

E. 0

+









+

EXERCISE: Summing a List

+

Write a function sumList such that sumList [x1, ..., xn] returns x1 + ... + xn

+
sumList :: [Int] -> Int
+sumList = ???
+

When you are done you should get the following behavior:

+
>>> sumList []
+0
+
+>>> sumlist [3]
+3
+
+>>> sumlist [2, 3]
+5
+
+>>> sumlist [1, 2, 3]
+6
+

Recap

+

+
    +
  • Core program element is an expression
  • +
  • Every valid expression has a type (determined at compile-time)
  • +
  • Every valid expression reduces to a value (computed at run-time)
  • +
+

Execution

+
    +
  • Basic values & operators

  • +
  • Execution / Function Calls just substitute equals by equals

  • +
  • Pack data into tuples & lists

  • +
  • Unpack data via pattern-matching

  • +
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/lectures/02-haskell.html b/docs/lectures/02-haskell.html new file mode 100644 index 0000000..a41c44e --- /dev/null +++ b/docs/lectures/02-haskell.html @@ -0,0 +1,839 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

Haskell Crash Course Part I

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

From the Lambda Calculus to Haskell

+














+

What is Haskell?

+


+

A typed, lazy, purely functional programming language

+


+

Haskell = λ-calculus ++

+
    +
  • better syntax
  • +
  • types
  • +
  • built-in features +
      +
    • booleans, numbers, characters
    • +
    • records (tuples)
    • +
    • lists
    • +
    • recursion
    • +
    • +
  • +
+







+

Programming in Haskell

+

Computation by Calculation

+


+

Substituting equals by equals

+













+

Computation via Substituting Equals by Equals

+

+    (1 + 3) * (4 + 5)
+                        -- subst 1 + 3 = 4
+==>       4 * (4 + 5)
+                        -- subst 4 + 5 = 9
+==>       4 * 9
+                        -- subst 4 * 9 = 36
+==>       36
+









+

Computation via Substituting Equals by Equals

+

Equality-Substitution enables Abstraction via Pattern Recognition

+











+

Abstraction via Pattern Recognition

+

Repeated Expressions

+
                31 * (42 + 56)
+                70 * (12 + 95)
+                90 * (68 + 12)
+

Recognize Pattern as λ-function

+
pat = \x y z -> x  * ( y + z ) 
+

Equivalent Haskell Definition

+
pat   x y z =  x  * ( y + z ) 
+

Function Call is Pattern Instance

+
pat 31 42 56 =*> 31 * (42 + 56) =*> 31 * 98  =*> 3038
+pat 70 12 95 =*> 70 * (12 + 95) =*> 70 * 107 =*> 7490
+pat 90 68 12 =*> 90 * (68 + 12) =*> 90 * 80  =*> 7200
+

Key Idea: Computation is substitute equals by equals.

+











+

Programming in Haskell

+

Substitute Equals by Equals

+



+

Thats it! (Do not think of registers, stacks, frames etc.)

+








+

Elements of Haskell

+

+
    +
  • Core program element is an expression
  • +
  • Every valid expression has a type (determined at compile-time)
  • +
  • Every valid expression reduces to a value (computed at run-time)
  • +
+

Ill-typed* expressions are rejected at compile-time before execution

+
    +
  • like in Java
  • +
  • not like λ-calculus or Python …
  • +
+
weirdo = 1 0     -- rejected by GHC
+









+

Why are types good?

+
    +
  • Helps with program design
  • +
  • Types are contracts (ignore ill-typed inputs!)
  • +
  • Catches errors early
  • +
  • Allows compiler to generate code
  • +
  • Enables compiler optimizations
  • +
+











+

The Haskell Eco-System

+
    +
  • Batch compiler: ghc Compile and run large programs

  • +
  • Interactive Shell ghci Shell to interactively run small programs online

  • +
  • Build Tool stack Build tool to manage libraries etc.

  • +
+









+

Interactive Shell: ghci

+
$ stack ghci
+
+:load file.hs
+:type expression 
+:info variable
+









+

A Haskell Source File

+

A sequence of top-level definitions x1, x2, …

+
    +
  • Each has type type_1, type_2, …

  • +
  • Each defined by expression expr_1, expr_2, …

  • +
+
x_1 :: type_1
+x_1 = expr_1
+
+x_2 :: type_2
+x_2 = expr_2
+
+.
+.
+.
+









+

Basic Types

+
ex1 :: Int
+ex1 = 31 * (42 + 56)   -- this is a comment
+
+ex2 :: Double
+ex2 = 3 * (4.2 + 5.6)  -- arithmetic operators "overloaded"
+
+ex3 :: Char 
+ex3 = 'a'              -- 'a', 'b', 'c', etc. built-in `Char` values
+
+ex4 :: Bool 
+ex4 = True             -- True, False are builtin Bool values
+
+ex5 :: Bool 
+ex5 = False
+









+

QUIZ: Basic Operations

+
ex6 :: Int
+ex6 = 4 + 5
+
+ex7 :: Int
+ex7 = 4 * 5
+
+ex8 :: Bool
+ex8 = 5 > 4 
+
+quiz :: ???
+quiz = if ex8 then ex6 else ex7
+

What is the type of quiz?

+

A. Int

+

B. Bool

+

C. Error!

+









+

QUIZ: Basic Operations

+
ex6 :: Int
+ex6 = 4 + 5
+
+ex7 :: Int
+ex7 = 4 * 5
+
+ex8 :: Bool
+ex8 = 5 > 4 
+
+quiz :: ???
+quiz = if ex8 then ex6 else ex7
+

What is the value of quiz?

+

A. 9

+

B. 20

+

C. Other!

+









+

Function Types

+

In Haskell, a function is a value that has a type

+
A -> B
+

A function that

+
    +
  • takes input of type A
  • +
  • returns output of type B
  • +
+

For example

+
isPos :: Int -> Bool
+isPos = \n -> (x > 0)
+

Define function-expressions using \ like in λ-calculus!

+

But Haskell also allows us to put the parameter on the left

+
isPos :: Int -> Bool
+isPos n = (x > 0)
+

(Meaning is identical to above definition with \n -> ...)

+









+

Multiple Argument Functions

+

A function that

+
    +
  • takes three inputs A1, A2 and A3
  • +
  • returns one output B has the type
  • +
+
A1 -> A2 -> A3 -> B
+

For example

+
pat :: Int -> Int -> Int -> Int
+pat = \x y z -> x * (y + z)
+

which we can write with the params on the left as

+
pat :: Int -> Int -> Int -> Int
+pat x y z = x * (y + z)
+









+

QUIZ

+

What is the type of quiz ?

+
quiz :: ???
+quiz x y = (x + y) > 0
+

A. Int -> Int

+

B. Int -> Bool

+

C. Int -> Int -> Int

+

D. Int -> Int -> Bool

+

E. (Int, Int) -> Bool

+









+

Function Calls

+

A function call is exactly like in the λ-calculus

+
e1 e2
+

where e1 is a function and e2 is the argument. For example

+
>>> isPos 12
+True 
+
+>>> isPos (0 - 5)
+False
+









+

Multiple Argument Calls

+

With multiple arguments, just pass them in one by one, e.g.

+
(((e e1) e2) e3)
+

For example

+
>>> pat 31 42 56 
+3038
+









+

EXERCISE

+

Write a function myMax that returns the maximum of two inputs

+
myMax :: Int -> Int -> Int
+myMax = ???
+

When you are done you should see the following behavior:

+
>>> myMax 10 20
+20
+
+>>> myMax 100 5
+100
+









+

EXERCISE

+

Write a function sumTo such that sumTo n evaluates to 0 + 1 + 2 + ... + n

+
sumTo :: Int -> Int
+sumTo n = ???
+

When you are done you should see the following behavior:

+
>>> sumTo 3
+6 
+>>> sumTo 4
+10 
+>>> sumTo 5
+15
+









+

How to Return Multiple Outputs?

+









+

Tuples

+

A type for packing n different kinds of values into a single “struct”

+
(T1,..., Tn)
+

For example

+
tup1 :: ???
+tup1 = ('a', 5) 
+
+tup2 :: (Char, Double, Int)
+tup2 = ('a', 5.2, 7) 
+









+

QUIZ

+

What is the type ??? of tup3?

+
tup3 :: ???
+tup3 = ((7, 5.2), True)
+

A. (Int, Bool)

+

B. (Int, Double, Bool)

+

C. (Int, (Double, Bool))

+

D. ((Int, Double), Bool)

+

E. (Tuple, Bool)

+









+

Extracting Values from Tuples

+

We can create a tuple of three values e1, e2, and e3

+
tup = (e1, e2, e3)
+

… but how to extract the values from this tuple?

+

Pattern Matching via case-of expressions

+
fst3 :: (t1, t2, t3) -> t1
+fst3 t = case t of 
+           (x1, x2, x3) -> x1
+
+snd3 :: (t1, t2, t3) -> t2
+snd3 t = case t of 
+           (x1, x2, x3) -> x2
+
+thd3 :: (t1, t2, t3) -> t3
+thd3 t = case t of 
+           (x1, x2, x3) -> x3
+









+

QUIZ

+

What is the value of quiz defined as

+
tup2 :: (Char, Double, Int)
+tup2 = ('a', 5.2, 7) 
+
+snd3 :: (t1, t2, t3) -> t2
+snd3 t = case t of 
+           (x1, x2, x3) -> x2
+
+quiz = snd3 tup2
+

A. 'a'

+

B. 5.2

+

C. 7

+

D. ('a', 5.2)

+

E. (5.2, 7)

+









+

Lists

+

Unbounded Sequence of values of type T

+
[T]
+

For example

+
chars :: [Char]
+chars = ['a', 'b', 'c'] 
+
+ints :: [Int]
+ints = [1, 3, 5, 7] 
+
+pairs :: [(Int, Bool)]
+pairs = [(1,True), (2,False)]
+









+

QUIZ

+

What is the type of things defined as

+
things :: ???
+things = [ [1], [2, 3], [4, 5, 6] ] 
+

A. [Int]

+

B. ([Int], [Int], [Int])

+

C. [(Int, Int, Int)]

+

D. [[Int]]

+

E. List

+









+

List’s Values Must Have The SAME Type!

+

The type [T] denotes an unbounded sequence of values of type T

+

Suppose you have a list

+
oops = [1, 2, 'c']
+

There is no T that we can use

+
    +
  • As last element is not Int
  • +
  • First two elements are not Char!
  • +
+

Result: Mysterious Type Error!

+









+

Constructing Lists

+

There are two ways to construct lists

+
    []     -- creates an empty list 
+    h:t    -- creates a list with "head" 'h' and "tail" t 
+

For example

+
>>> 3 : []
+[3]
+
+>>> 2 : (3 : []) 
+[2, 3]
+
+>>> 1 : (2 : (3 : [])) 
+[1, 2, 3]
+

Cons Operator : is Right Associative

+

x1 : x2 : x3 : x4 : t means x1 : (x2 : (x3 : (x4 : t)))

+

So we can just avoid the parentheses.

+

Syntactic Sugar

+

Haskell lets you write [x1, x2, x3, x4] instead of x1 : x2 : x3 : x4 : []

+









+

Functions Producing Lists

+

Lets write a function copy3 that

+
    +
  • takes an input x and
  • +
  • returns a list with three copies of x
  • +
+
copy3 :: ???
+copy3 x = ???
+

When you are done, you should see the following

+
>>> copy3 5
+[5, 5, 5]
+
+>>> copy3 "cat"
+["cat", "cat", "cat"]
+









+

Lets write some Functions

+

A Recipe

+

Step 1: Write some tests

+

Step 2: Write the type

+

Step 3: Write the code

+












+

PRACTICE: Clone

+

Write a function clone such that clone n x returns a list with n copies of x.

+

1. Tests

+

When you are done you should see the following behavior

+
>>> clone 0 "cat" 
+[]
+
+>>> clone 1 "cat" 
+["cat"]
+
+>>> clone 2 "cat" 
+["cat", "cat"]
+
+>>> clone 3 "cat" 
+["cat", "cat", "cat"]
+
+>>> clone 3 100
+[100, 100, 100]
+

2. Types

+
clone :: ???
+

3. Code

+
clone n x = ???
+















+

How does clone execute?

+

(Substituting equals-by-equals!)

+
clone 3 100
+  =*> ???
+














+

EXERCISE: Range

+

Write a function range such that range i j returns the list of values [i, i+1, ..., j]

+
range :: ???
+range i j = ???
+

1. Tests

+
>>> range 4 3 
+[]
+
+>>> range 3 3 
+[3]
+
+>>> range 2 3 
+[2, 3]
+
+>>> range 1 3 
+[1, 2, 3]
+
+>>> range 0 3 
+[0, 1, 2, 3]
+

2. Type

+
range :: ???
+

3. Code

+
range = ???
+









+

Functions Consuming Lists

+

So far: how to produce lists.

+

Next how to consume lists!

+








+









+

EXERCISE

+

Lets write a function firstElem such that firstElem xs returns the first element xs if it is a non-empty list, and 0 otherwise.

+

HINT: How to extract values from a list?

+

1. Tests

+

When you are done you should see the following behavior:

+
>>> firstElem []
+0
+
+>>> firstElem [10, 20, 30]
+10
+
+>>> firstElem [5, 6, 7, 8] 
+5
+

2. Type

+
firstElem :: ???
+

3. Code

+
firstElem = ???
+









+

QUIZ

+

Suppose we have the following mystery function

+
mystery :: [a] -> Int
+mystery l = case l of 
+              []     -> 0
+              (x:xs) -> 1 + mystery xs
+

What does mystery [10, 20, 30] evaluate to?

+

A. 10

+

B. 20

+

C. 30

+

D. 3

+

E. 0

+









+

EXERCISE: Summing a List

+

Write a function sumList such that sumList [x1, ..., xn] returns x1 + ... + xn

+

1. Tests

+

When you are done you should get the following behavior:

+
>>> sumList []
+0
+
+>>> sumlist [3]
+3
+
+>>> sumlist [2, 3]
+5
+
+>>> sumlist [1, 2, 3]
+6
+

2. Type

+
sumList :: [Int] -> Int
+

3. Code

+
sumList = ???
+

Functions on lists: take

+

Let’s write a function to take first n elements of a list xs.

+

1. Tests

+
-- >>> ???
+

2. Type

+
take :: ???
+

Some useful library functions

+
-- | Length of the list
+length :: [t] -> Int
+
+-- | Append two lists
+(++) :: [t] -> [t] -> [t]
+
+-- | Are two lists equal?
+(==) :: [t] -> [t] -> Bool
+


+

You can search for library functions on Hoogle!

+







+

+**3. Code**
+
+```haskell
+take = ???
+

Some useful library functions

+
-- | Length of the list
+length :: [t] -> Int
+
+-- | Append two lists
+(++) :: [t] -> [t] -> [t]
+
+-- | Are two lists equal?
+(==) :: [t] -> [t] -> Bool
+


+

You can search for library functions on Hoogle!

+







+

Recap

+

+
    +
  • Core program element is an expression
  • +
  • Every valid expression has a type (determined at compile-time)
  • +
  • Every valid expression reduces to a value (computed at run-time)
  • +
+

Execution

+
    +
  • Basic values & operators

  • +
  • Execution / Function Calls just substitute equals by equals

  • +
  • Pack data into tuples & lists

  • +
  • Unpack data via pattern-matching

  • +
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/lectures/03-datatypes.html b/docs/lectures/03-datatypes.html new file mode 100644 index 0000000..b8ff2c8 --- /dev/null +++ b/docs/lectures/03-datatypes.html @@ -0,0 +1,1024 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

Datatypes and Recursion

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

Plan for this week

+

Last week:

+
    +
  • built-in data types +
      +
    • base types, tuples, lists (and strings)
    • +
  • +
  • writing functions using pattern matching and recursion
  • +
+

This week:

+

+
    +
  • user-defined data types +
      +
    • and how to manipulate them using pattern matching and recursion
    • +
  • +
  • more details about recursion
  • +
+







+

Representing complex data

+

Previously, we’ve seen:

+
    +
  • base types: Bool, Int, Integer, Float

  • +
  • some ways to build up types: given types T1, T2

    +
      +
    • functions: T1 -> T2
    • +
    • tuples: (T1, T2)
    • +
    • lists: [T1]
    • +
  • +
+



+

Next: Algebraic Data Types:

+

A single, powerful way to type complex data

+
    +
  • Lets you define your own data types

  • +
  • Tuples and lists are special cases

  • +
+










+

Building data types

+


+

Three key ways to build complex types/values:

+
    +
  1. Product types (each-of): a value of T contains a value of T1 and a value of T2

  2. +
  3. Sum types (one-of): a value of T contains a value of T1 or a value of T2

  4. +
  5. Recursive types: a value of T contains a sub-value of the same type T

  6. +
+










+

Product types

+

Tuples can do the job but there are two problems…

+
deadlineDate :: (Int, Int, Int)
+deadlineDate = (1, 28, 2022)
+
+deadlineTime :: (Int, Int, Int)
+deadlineTime = (11, 59, 59)
+
+-- | Deadline date extended by one day
+extendDate :: (Int, Int, Int) -> (Int, Int, Int)
+extendDate = ...
+

Can you spot them?

+







+

1. Verbose and unreadable

+

A type synonym for T: a name that can be used interchangeably with T

+
type Date = (Int, Int, Int)
+type Time = (Int, Int, Int)
+
+deadlineDate :: Date
+deadlineDate = (1, 28, 2021)
+
+deadlineTime :: Time
+deadlineTime = (11, 59, 59)
+
+-- | Deadline date extended by one day
+extendDate :: Date -> Date
+extendDate = ...
+







+

2. Unsafe

+

We want to catch this error at compile time!!!

+
extension deadlineTime
+


+

Solution: construct two different datatypes

+
data Date    = Date Int Int Int
+data Time    = Time Int Int Int
+                 ^    ^---^---^---- parameter types
+                 `---------------- constructor name
+
+deadlineDate :: Date
+deadlineDate = Date 2 7 2020
+
+deadlineTime :: Time
+deadlineTime = Time 11 59 59
+










+

Record syntax

+

Haskell’s record syntax allows you to name the constructor parameters:

+
    +
  • Instead of

    +
    data Date = Date Int Int Int
  • +
  • you can write:

    +
    data Date = Date
    +  { month :: Int
    +  , day   :: Int
    +  , year  :: Int  
    +  }
  • +
  • then you can do:

    +
    deadlineDate = Date 2 4 2019
    +
    +deadlineMonth = month deadlineDate -- use field name as a function
  • +
+










+

Building data types

+


+

Three key ways to build complex types/values:

+
    +
  1. Product types (each-of): a value of T contains a value of T1 and a value of T2 [done]

  2. +
  3. Sum types (one-of): a value of T contains a value of T1 or a value of T2

  4. +
  5. Recursive types: a value of T contains a sub-value of the same type T

  6. +
+










+

Example: NanoMarkdown

+

Suppose I want to represent a text document with simple markup

+

Each paragraph is either:

+
    +
  • plain text (String)
  • +
  • heading: level and text (Int and String)
  • +
  • list: ordered? and items (Bool and [String])
  • +
+

I want to store all paragraphs in a list

+
doc = [ (1, "Notes from 130")                   -- Level 1 heading
+      , "There are two types of languages:"     -- Plain text
+      , (True, [ "those people complain about"  -- Ordered list
+               , "those no one uses"])
+      ]
+

But this does not type check!!!

+










+

Sum Types

+

Solution: construct a new type for paragraphs that is a sum (one-of) the three options!

+

Each paragraph is either:

+
    +
  • plain text (String)
  • +
  • heading: level and text (Int and String)
  • +
  • list: ordered? and items (Bool and [String])
  • +
+
data Paragraph              -- ^ THREE constructors, w/ different parameters
+  = PText    String         -- ^ text: plain string
+  | PHeading Int   String   -- ^ head: level and text (Int & String)
+  | PList    Bool [String]  -- ^ list: ordered? & items (Bool & [String])
+










+

QUIZ

+
data Paragraph
+  = PText String
+  | PHeading Int String
+  | PList Bool [String]
+

What is the type of Text "Hey there!"? i.e. How would GHCi reply to:

+
>:t (PText "Hey there!")
+

A. Syntax error

+

B. Type error

+

C. PText

+

D. String

+

E. Paragraph

+


+









+

Constructing datatypes

+
data T
+  = C1 T11 ... T1k
+  | C2 T21 ... T2l
+  | ... 
+  | Cn Tn1 ... Tnm
+
    +
  • T is the new datatype

  • +
  • C1 .. Cn are the constructors of T

  • +
+


+

A value of type T is

+
    +
  • either C1 v1 .. vk with vi :: T1i
  • +
  • or C2 v1 .. vl with vi :: T2i
  • +
  • or
  • +
  • or Cn v1 .. vm with vi :: Tni
  • +
+


+

You can think of a T value as a box:

+
    +
  • either a box labeled C1 with values of types T11 .. T1k inside
  • +
  • or a box labeled C2 with values of types T21 .. T2l inside
  • +
  • or
  • +
  • or a box labeled Cn with values of types Tn1 .. Tnm inside
  • +
+
+
One-of Types
+
+















+

Constructing datatypes: Paragraph

+
data Paragraph
+  = PText String
+  | PHeading Int String
+  | PList Bool [String]
+

Apply a constructor = pack some values into a box (and label it)

+
    +
  • PText "Hey there!" +
      +
    • put "Hey there!" in a box labeled PText
    • +
  • +
  • PHeading 1 "Introduction" +
      +
    • put 1 and "Introduction" in a box labeled PHeading
    • +
  • +
  • Boxes have different labels but same type (Paragraph)
  • +
+
+
The Paragraph Type
+
+

with example values:

+
+
The Paragraph Type
+
+










+

QUIZ

+
data Paragraph
+  = PText String
+  | PHeading Int String
+  | PList Bool [String]
+

What would GHCi say to

+
>:t [PHeading 1 "Introduction", PText "Hey there!"]
+

A. Syntax error

+

B. Type error

+

C. Paragraph

+

D. [Paragraph]

+

E. [String]

+


+









+

Example: NanoMD

+
data Paragraph
+  = PText String
+  | PHeading Int String
+  | PList Bool [String]
+

Now I can create a document like so:

+
doc :: [Paragraph]
+doc = [ PHeading 1 "Notes from 130"
+      , PText "There are two types of languages:"
+      , PList True [ "those people complain about"
+                   , "those no one uses"
+                   ])
+      ]
+













+

Problem: How to Convert Documents to HTML?

+

How to write a function

+
html :: Paragraph -> String
+html p = ???      -- ^ depends on the kind of paragraph!
+



+

How to tell what’s in the box?

+
    +
  • Look at the label!
  • +
+










+

Pattern matching

+

Pattern matching = looking at the label and extracting values from the box

+
    +
  • we’ve seen it before
  • +
  • but now for arbitrary datatypes
  • +
+
html :: Paragraph -> String
+html p = case p of
+           PText str        -> ...  -- It's a plain text; str :: String 
+           PHeading lvl str -> ...  -- It's a heading;    lvl :: Int, str :: String
+           PList ord items  -> ...  -- It's a list;       ord :: Bool, items :: [String]
+


+

or, we can pull the case-of to the “top” as

+
html :: Paragraph -> String
+html (PText str)        = ...  -- It's a plain text; str :: String 
+html (PHeading lvl str) = ...  -- It's a heading;    lvl :: Int, str :: String
+html (PList ord items)  = ...  -- It's a list;       ord :: Bool, items :: [String]
+





+
html :: Paragraph -> String
+html (PText str)            -- It's a plain text! Get string
+  = unlines [open "p", str, close "p"]
+
+html (PHeading lvl str)     -- It's a heading! Get level and string
+  = let htag = "h" ++ show lvl
+    in unwords [open htag, str, close htag]
+
+html (PList ord items)      -- It's a list! Get ordered and items
+  = let ltag   = if ord then "ol" else "ul"
+        litems = [unwords [open "li", i, close "li"] | i <- items]
+    in unlines ([open ltag] ++ litems ++ [close ltag])
+







+

Dangers of pattern matching (1)

+
html :: Paragraph -> String
+html (PText str) = ...
+html (PList ord items) = ...
+

What would GHCi say to:

+
html (PHeading 1 "Introduction")
+


+

Dangers of pattern matching (2)

+
html :: Paragraph -> String
+html (PText str)        = unlines [open "p", str, close "p"]
+html (PHeading lvl str) = ...
+html (PHeading 0 str)   = html (PHeading 1 str)
+html (PList ord items)  = ...
+

What would GHCi say to:

+
html (PHeading 0 "Introduction")
+


+






+

Dangers of pattern matching

+

Beware of missing and overlapped patterns

+
    +
  • GHC warns you about overlapped patterns
  • +
  • GHC warns you about missing patterns when called with -W (use :set -W in GHCi)
  • +
+










+

Pattern-Match Expression

+

Everything is an expression?

+

+

We’ve seen: pattern matching in equations

+

Actually, pattern-match is also an expression

+
html :: Paragraph -> String
+html p = case p of
+           PText    str       -> unlines [open "p", str, close "p"]
+           PHeading lvl str   -> ...
+           PList    ord items -> ...
+

The code we saw earlier was syntactic sugar

+
html (C1 x1 ...) = e1
+html (C2 x2 ...) = e2
+html (C3 x3 ...) = e3
+

is just for humans, internally represented as a case-of expression

+
html p = case p of
+           (C1 x1 ...) -> e1
+           (C2 x2 ...) -> e2
+           (C3 x3 ...) -> e3
+










+

QUIZ

+

What is the type of

+
let p = Text "Hey there!"
+in case p of
+    PText    str   -> str
+    PHeading lvl _ -> lvl
+    PList    ord _ -> ord
+

A. Syntax error

+

B. Type error

+

C. String

+

D. Paragraph

+

E. Paragraph -> String

+


+









+

Pattern matching expression: typing

+

The case expression

+
case e of
+  pattern1 -> e1
+  pattern2 -> e2
+  ...
+  patternN -> eN
+

has type T if

+
    +
  • each e1eN has type T
  • +
  • e has some type D
  • +
  • each pattern1patternN is a valid pattern for D +
      +
    • i.e. a variable or a constructor of D applied to other patterns
    • +
  • +
+

The expression e is called the match scrutinee

+










+

QUIZ

+

What is the type of

+
let p = Text "Hey there!"
+in case p of
+    PText _      -> 1
+    PHeading _ _ -> 2
+    PList _ _    -> 3
+

A. Syntax error

+

B. Type error

+

C. Paragraph

+

D. Int

+

E. Paragraph -> Int

+


+









+

Building data types

+


+

+


+

Three key ways to build complex types/values:

+
    +
  1. Product types (each-of): a value of T contains a value of T1 and a value of T2 [done]

    +
      +
    • Cartesian product of two sets: v(T) = v(T1) × v(T2)
    • +
  2. +
  3. Sum types (one-of): a value of T contains a value of T1 or a value of T2 [done]

    +
      +
    • Union (sum) of two sets: v(T) = v(T1) ∪ v(T2)
    • +
  4. +
  5. Recursive types: a value of T contains a sub-value of the same type T

  6. +
+










+

Recursive types

+

Let’s define natural numbers from scratch:

+
data Nat = ???
+








+
data Nat = Zero | Succ Nat
+

A Nat value is:

+
    +
  • either an empty box labeled Zero
  • +
  • or a box labeled Succ with another Nat in it!
  • +
+

Some Nat values:

+
Zero                     -- 0
+Succ Zero                -- 1
+Succ (Succ Zero)         -- 2
+Succ (Succ (Succ Zero))  -- 3
+...
+










+

Functions on recursive types

+

Recursive code mirrors recursive data

+

1. Recursive type as a parameter

+
data Nat = Zero     -- base constructor
+         | Succ Nat -- inductive constructor
+

Step 1: add a pattern per constructor

+
toInt :: Nat -> Int
+toInt Zero     = ... -- base case
+toInt (Succ n) = ... -- inductive case
+                     -- (recursive call goes here)
+

Step 2: fill in base case:

+
toInt :: Nat -> Int
+toInt Zero     = 0   -- base case
+toInt (Succ n) = ... -- inductive case
+                     -- (recursive call goes here)
+

Step 2: fill in inductive case using a recursive call:

+
toInt :: Nat -> Int
+toInt Zero     = 0           -- base case
+toInt (Succ n) = 1 + toInt n -- inductive case
+










+

QUIZ

+

What does this evaluate to?

+
let foo i = if i <= 0 then Zero else Succ (foo (i - 1))
+in foo 2 
+

A. Syntax error

+

B. Type error

+

C. 2

+

D. Succ Zero

+

E. Succ (Succ Zero)

+


+









+

2. Recursive type as a result

+
data Nat = Zero     -- base constructor
+         | Succ Nat -- inductive constructor
+
+         
+fromInt :: Int -> Nat
+fromInt n
+  | n <= 0    = Zero                   -- base case
+  | otherwise = Succ (fromInt (n - 1)) -- inductive case
+                                       -- (recursive call goes here)
+







+

EXERCISE: Putting the two together

+
data Nat = Zero     -- base constructor
+         | Succ Nat -- inductive constructor
+
+         
+add :: Nat -> Nat -> Nat
+add n m = ???
+
+sub :: Nat -> Nat -> Nat
+sub n m = ???
+










+

EXERCISE: Putting the two together

+
data Nat = Zero     -- base constructor
+         | Succ Nat -- inductive constructor
+
+         
+add :: Nat -> Nat -> Nat
+add n m = ???
+










+
data Nat = Zero     -- base constructor
+         | Succ Nat -- inductive constructor
+
+         
+add :: Nat -> Nat -> Nat
+add Zero     m = ???            -- base case
+add (Succ n) m = ???            -- inductive case
+










+

EXERCISE: Putting the two together

+
data Nat = Zero     -- base constructor
+         | Succ Nat -- inductive constructor
+
+sub :: Nat -> Nat -> Nat
+sub n m = ???
+










+
sub :: Nat -> Nat -> Nat
+sub n        Zero     = ???     -- base case 1
+sub Zero     _        = ???     -- base case 2
+sub (Succ n) (Succ m) = ???     -- inductive case
+










+

Lesson: Recursive code mirrors recursive data

+
    +
  • Which of multiple arguments should you recurse on?

  • +
  • Key: Pick the right inductive strategy!

  • +
+


+

(easiest if there is a single argument of course…)

+










+

Example: Calculator

+

I want to implement an arithmetic calculator to evaluate expressions like:

+
    +
  • 4.0 + 2.9
  • +
  • 3.785.92
  • +
  • (4.0 + 2.9) * (3.78 - 5.92)
  • +
+

What is a Haskell datatype to represent these expressions?

+
data Expr = ???
+









+
data Expr = Num Float
+          | Add Expr Expr
+          | Sub Expr Expr
+          | Mul Expr Expr
+



+

We can represent expressions as

+
e0, e1, e2 :: Expr
+e0 = Add (Num 4.0)  (Num 2.9)
+e1 = Sub (Num 3.78) (Num 5.92)
+e2 = Mul e0 e1
+







+

EXERCISE: Expression Evaluator

+

Write a function to evaluate an expression.

+
-- >>> eval (Add (Num 4.0)  (Num 2.9))
+-- 6.9
+
+eval :: Expr -> Float
+eval e = ???
+









+

Recursion is…

+

Building solutions for big problems from solutions for sub-problems

+
    +
  • Base case: what is the simplest version of this problem and how do I solve it?
  • +
  • Inductive strategy: how do I break down this problem into sub-problems?
  • +
  • Inductive case: how do I solve the problem given the solutions for subproblems?
  • +
+









+

Lists

+

Lists aren’t built-in! They are an algebraic data type like any other:

+
data List 
+  = Nil           -- ^ base constructor
+  | Cons Int List -- ^ inductive constructor
+
    +
  • List [1, 2, 3] is represented as Cons 1 (Cons 2 (Cons 3 Nil))

  • +
  • Built-in list constructors [] and (:) are just fancy syntax for Nil and Cons

  • +
+



+

Functions on lists follow the same general strategy:

+
length :: List -> Int
+length Nil         = 0              -- base case
+length (Cons _ xs) = 1 + length xs  -- inductive case
+











+

EXERCISE: Appending Lists

+

What is the right inductive strategy for appending two lists?

+
-- >>> append (Cons 1 (Cons 2 (Cons 3 Nil))) (Cons 4 (Cons 5 (Cons 6 Nil)))
+-- (Cons 1 (Cons 2 (Cons 3 (Cons 4 (Cons 5 (Cons 6 Nil))))))
+
append :: List -> List -> List
+append xs ys = ??
+










+

Trees

+

Lists are unary trees with elements stored in the nodes:

+
+
Lists are unary trees
+
+
data List = Nil | Cons Int List
+

How do we represent binary trees with elements stored in the nodes?

+
+
Binary trees with data at nodes
+
+










+

QUIZ: Binary trees I

+

What is a Haskell datatype for binary trees with elements stored in the nodes?

+
+
Binary trees with data at nodes
+
+

(A) data Tree = Leaf | Node Int Tree

+

(B) data Tree = Leaf | Node Tree Tree

+

(C) data Tree = Leaf | Node Int Tree Tree

+

(D) data Tree = Leaf Int | Node Tree Tree

+

(E) data Tree = Leaf Int | Node Int Tree Tree

+


+










+
+
Binary trees with data at nodes
+
+
data Tree = Leaf | Node Int Tree Tree
+
+t1234 = Node 1 
+          (Node 2 (Node 3 Leaf Leaf) Leaf) 
+          (Node 4 Leaf Leaf)
+








+

Functions on trees

+
depth :: Tree -> Int
+depth t = ??
+








+

QUIZ: Binary trees II

+

What is a Haskell datatype for binary trees with elements stored in the leaves?

+
+
Binary trees with data at leaves
+
+

(A) data Tree = Leaf | Node Int Tree

+

(B) data Tree = Leaf | Node Tree Tree

+

(C) data Tree = Leaf | Node Int Tree Tree

+

(D) data Tree = Leaf Int | Node Tree Tree

+

(E) data Tree = Leaf Int | Node Int Tree Tree

+


+








+
data Tree = Leaf Int | Node Tree Tree
+
+t12345 = Node
+          (Node (Node (Leaf 1) (Leaf 2)) (Leaf 3))
+          (Node (Leaf 4) (Leaf 5))
+










+

Why use Recursion?

+
    +
  1. Often far simpler and cleaner than loops

    +
      +
    • But not always…
    • +
  2. +
  3. Structure often forced by recursive data

  4. +
  5. Forces you to factor code into reusable units (recursive functions)

  6. +
+









+

Why not use Recursion?

+
    +
  1. Slow

  2. +
  3. Can cause stack overflow

  4. +
+









+

Example: factorial

+
fac :: Int -> Int
+fac n
+  | n <= 1    = 1
+  | otherwise = n * fac (n - 1)
+



+

Lets see how fac 4 is evaluated:

+
<fac 4>
+  ==> <4 * <fac 3>>              -- recursively call `fact 3`
+  ==> <4 * <3 * <fac 2>>>        --   recursively call `fact 2`
+  ==> <4 * <3 * <2 * <fac 1>>>>  --     recursively call `fact 1`
+  ==> <4 * <3 * <2 * 1>>>        --     multiply 2 to result 
+  ==> <4 * <3 * 2>>              --   multiply 3 to result
+  ==> <4 * 6>                    -- multiply 4 to result
+  ==> 24
+



+

Each function call <> allocates a frame on the call stack

+
    +
  • expensive
  • +
  • the stack has a finite size
  • +
+

Can we do recursion without allocating stack frames?

+









+

Tail Recursion

+

Recursive call is the top-most sub-expression in the function body

+
    +
  • i.e. no computations allowed on recursively returned value

  • +
  • i.e. value returned by the recursive call == value returned by function

  • +
+



+

QUIZ: Is this function tail recursive?

+
fac :: Int -> Int
+fac n
+  | n <= 1    = 1
+  | otherwise = n * fac (n - 1)
+

A. Yes

+

B. No

+


+








+

Tail recursive factorial

+

Let’s write a tail-recursive factorial!

+
facTR :: Int -> Int
+facTR n = ... 
+

HINT: Lets first write it with a loop

+





+

Lets see how facTR is evaluated:

+
<facTR 4>
+  ==>    <<loop 1  4>> -- call loop 1 4
+  ==>   <<<loop 4  3>>> -- rec call loop 4 3 
+  ==>  <<<<loop 12 2>>>> -- rec call loop 12 2
+  ==> <<<<<loop 24 1>>>>> -- rec call loop 24 1
+  ==> 24                  -- return result 24! 
+

Each recursive call directly returns the result

+
    +
  • without further computation

  • +
  • no need to remember what to do next!

  • +
  • no need to store the “empty” stack frames!

  • +
+









+

Why care about Tail Recursion?

+

Because the compiler can transform it into a fast loop

+
facTR n = loop 1 n
+  where
+    loop acc n
+      | n <= 1    = acc
+      | otherwise = loop (acc * n) (n - 1)
+


+
function facTR(n){ 
+  var acc = 1;
+  while (true) {
+    if (n <= 1) { return acc ; }
+    else        { acc = acc * n; n = n - 1; }
+  }
+}
+
    +
  • Tail recursive calls can be optimized as a loop

    +
      +
    • no stack frames needed!
    • +
  • +
  • Part of the language specification of most functional languages

    +
      +
    • compiler guarantees to optimize tail calls
    • +
  • +
+






+

That’s all folks!

+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/lectures/04-hof.html b/docs/lectures/04-hof.html new file mode 100644 index 0000000..b1394ff --- /dev/null +++ b/docs/lectures/04-hof.html @@ -0,0 +1,860 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

Higher-Order Functions

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

Plan for this week

+

Last week:

+
    +
  • user-defined data types

  • +
  • manipulating data-types with pattern matching and recursion

  • +
  • how to make recursive functions more efficient with tail recursion

  • +
+










+

The long arc of history

+

Pattern matching is a very old PL idea …

+ +

… but will finally be added to Python 3.10

+
    +
  • https://www.python.org/dev/peps/pep-0622/
  • +
+
def make_point_3d(pt):
+    match pt:
+        case (x, y):
+            return Point3d(x, y, 0)
+        case (x, y, z):
+            return Point3d(x, y, z)
+        case Point2d(x, y):
+            return Point3d(x, y, 0)
+        case Point3d(_, _, _):
+            return pt
+        case _:
+            raise TypeError("not a point we support")
+










+

Plan for this week

+

Last week:

+
    +
  • user-defined data types

  • +
  • manipulating data-types with pattern matching and recursion

  • +
  • how to make recursive functions more efficient with tail recursion

  • +
+

This week:

+
    +
  • code reuse with higher-order functions (HOFs)

  • +
  • some useful HOFs: map, filter, and fold

  • +
+










+

Recursion is good…

+
    +
  • Recursive code mirrors recursive data

    +
      +
    • Base constructor -> Base case
    • +
    • Inductive constructor -> Inductive case (with recursive call)
    • +
  • +
  • But it can get kinda repetitive!

  • +
+










+

Example: evens

+

Let’s write a function evens:

+
-- evens []        ==> []
+-- evens [1,2,3,4] ==> [2,4]
+
evens       :: [Int] -> [Int]
+evens []     = ... 
+evens (x:xs) = ...
+







+

Example: four-letter words

+

Let’s write a function fourChars:

+
-- fourChars [] ==> []
+-- fourChars ["i","must","do","work"] ==> ["must","work"]
+
fourChars :: [String] -> [String]
+fourChars []     = ... 
+fourChars (x:xs) = ...
+










+

Yikes! Most Code is the Same!

+

Lets rename the functions to foo:

+
foo []            = []
+foo (x:xs)
+  | x mod 2 == 0  = x : foo xs
+  | otherwise     =     foo xs
+
+foo []            = []
+foo (x:xs)
+  | length x == 4 = x : foo xs
+  | otherwise     =     foo xs
+

Only difference is condition

+
    +
  • x mod 2 == 0 vs length x == 4
  • +
+










+

Moral of the day

+


+

D.R.Y. Don’t Repeat Yourself!

+


+

Can we

+
    +
  • reuse the general pattern and

  • +
  • plug-in the custom condition?

  • +
+










+

Higher-Order Functions

+

General Pattern

+
    +
  • expressed as a higher-order function
  • +
  • takes plugin operations as arguments
  • +
+

Specific Operation

+
    +
  • passed in as an argument to the HOF
  • +
+







+

The “filter” pattern

+
+
The filter Pattern
+
+

General Pattern

+
    +
  • HOF filter
  • +
  • Recursively traverse list and pick out elements that satisfy a predicate
  • +
+

Specific Operations

+
    +
  • Predicates isEven and isFour
  • +
+
+
filter instances
+
+

Avoid duplicating code!

+










+

QUIZ: What is the type of filter?

+
-- evens [1,2,3,4] ==> [2,4]
+evens :: [Int] -> [Int]
+evens xs = filter isEven xs
+  where
+    isEven :: Int -> Bool
+    isEven x  =  x `mod` 2 == 0
+
+-- fourChars ["i","must","do","work"] ==> ["must","work"]
+fourChars :: [String] -> [String]
+fourChars xs = filter isFour xs
+  where
+    isFour :: String -> Bool
+    isFour x  =  length x == 4
+

So what’s the type of filter?

+
{- A -} filter :: (Int -> Bool) -> [Int] -> [Int]
+
+{- B -} filter :: (String -> Bool) -> [String] -> [String]
+
+{- C -} filter :: (a -> Bool) -> [a] -> [a]
+
+{- D -} filter :: (a -> Bool) -> [a] -> [Bool]
+
+{- E -} filter :: (a -> b) -> [a] -> [b]
+




















+

Type of filter

+
-- evens [1,2,3,4] ==> [2,4]
+evens :: [Int] -> [Int]
+evens xs = filter isEven xs
+  where
+    isEven :: Int -> Bool
+    isEven x  =  x `mod` 2 == 0
+
+-- fourChars ["i","must","do","work"] ==> ["must","work"]
+fourChars :: [String] -> [String]
+fourChars xs = filter isFour xs
+  where
+    isFour :: String -> Bool
+    isFour x  =  length x == 4
+

For any type a

+
    +
  • Input a predicate a -> Bool and collection [a]
  • +
  • Output a (smaller) collection [a]
  • +
+
filter :: (a -> Bool) -> [a] -> [a]
+

filter does not care what the list elements are

+
    +
  • as long as the predicate can handle them
  • +
+

filter is polymorphic (generic) in the type of list elements

+















+

Example: ALL CAPS!

+

Lets write a function shout:

+
-- shout []                    ==> []
+-- shout ['h','e','l','l','o'] ==> ['H','E','L','L','O'] 
+
shout :: [Char] -> [Char]
+shout []     = ...
+shout (x:xs) = ... 
+







+

Example: squares

+

Lets write a function squares:

+
-- squares []        ==> []
+-- squares [1,2,3,4] ==> [1,4,9,16] 
+
squares :: [Int] -> [Int]
+squares []     = ...
+squares (x:xs) = ...
+










+

Yikes, Most Code is the Same

+

Lets rename the functions to foo:

+
-- shout
+foo []     = []
+foo (x:xs) = toUpper x : foo xs
+
+-- squares
+foo []     = []
+foo (x:xs) = (x * x)   : foo xs
+



+

Lets refactor into the common pattern

+
pattern = ...
+







+

The “map” pattern

+
+
The map Pattern
+
+

General Pattern

+
    +
  • HOF map
  • +
  • Apply a transformation f to each element of a list
  • +
+

Specific Operations

+
    +
  • Transformations toUpper and \x -> x * x
  • +
+





+
map f []     = []
+map f (x:xs) = f x : map f xs
+

Lets refactor shout and squares

+
shout   = map ...
+
+squares = map ...
+





+
+
map instances
+
+










+

QUIZ

+

What is the type of map?

+
map f []     = []
+map f (x:xs) = f x : map f xs
+

(A) (Char -> Char) -> [Char] -> [Char]

+

(B) (Int -> Int) -> [Int] -> [Int]

+

(C) (a -> a) -> [a] -> [a]

+

(D) (a -> b) -> [a] -> [b]

+

(E) (a -> b) -> [c] -> [d]

+










+
-- For any types `a` and `b`
+--   if you give me a transformation from `a` to `b`
+--   and a list of `a`s,
+--   I'll give you back a list of `b`s 
+map :: (a -> b) -> [a] -> [b]
+


+

Type says it all!

+
    +
  • The only meaningful thing a function of this type can do is apply its first argument to elements of the list

  • +
  • Hoogle it!

  • +
+


+

Things to try at home:

+
    +
  • can you write a function map' :: (a -> b) -> [a] -> [b] whose behavior is different from map?

  • +
  • can you write a function map' :: (a -> b) -> [a] -> [b] such that map' f xs returns a list whose elements are not in map f xs?

  • +
+










+

QUIZ

+

What is the value of quiz?

+
map :: (a -> b) -> [a] -> [b]
+
+quiz = map (\(x, y) -> x + y) [1, 2, 3]
+

(A) [2, 4, 6]

+

(B) [3, 5]

+

(C) Syntax Error

+

(D) Type Error

+

(E) None of the above

+










+

Don’t Repeat Yourself

+

Benefits of factoring code with HOFs:

+
    +
  • Reuse iteration pattern

    +
      +
    • think in terms of standard patterns

    • +
    • less to write

    • +
    • easier to communicate

    • +
  • +
  • Avoid bugs due to repetition

  • +
+










+

Recall: length of a list

+
-- len []      ==> 0
+-- len ["carne","asada"] ==> 2
+len :: [a] -> Int
+len []     = 0
+len (x:xs) = 1 + len xs
+











+

Recall: summing a list

+
-- sum []      ==> 0
+-- sum [1,2,3] ==> 6
+sum :: [Int] -> Int
+sum []     = 0
+sum (x:xs) = x + sum xs
+











+

Example: string concatenation

+

Let’s write a function cat:

+
-- cat [] ==> ""
+-- cat ["carne","asada","torta"] ==> "carneasadatorta"
+cat :: [String] -> String
+cat []     = ...
+cat (x:xs) = ...
+













+

Can you spot the pattern?

+
-- len
+foo []     = 0
+foo (x:xs) = 1 + foo xs
+
+-- sum
+foo []     = 0
+foo (x:xs) = x + foo xs
+
+-- cat
+foo []     = ""
+foo (x:xs) = x ++ foo xs
+


+
pattern = ...
+










+

The “fold-right” pattern

+
+
The foldr Pattern
+
+

General Pattern

+
    +
  • Recurse on tail
  • +
  • Combine result with the head using some binary operation
  • +
+





+
foldr f b []     = b
+foldr f b (x:xs) = f x (foldr f b xs)
+



+

Let’s refactor sum, len and cat:

+
sum = foldr ...  ...
+
+cat = foldr ...  ...
+
+len = foldr ...  ...
+

Factor the recursion out!

+










+
+
foldr instances
+
+

You can write it more clearly as

+
sum = foldr (+) 0
+
+cat = foldr (++) ""
+










+

The “fold-right” pattern

+
foldr f b [a1, a2, a3, a4]
+  ==> f a1 (foldr f b [a2, a3, a4])
+  ==> f a1 (f a2 (foldr f b [a3, a4]))
+  ==> f a1 (f a2 (f a3 (foldr f b [a4])))
+  ==> f a1 (f a2 (f a3 (f a4 (foldr f b []))))
+  ==> f a1 (f a2 (f a3 (f a4 b)))
+

Accumulate the values from the right

+

For example:

+
foldr (+) 0 [1, 2, 3, 4]
+  ==> 1 + (foldr (+) 0 [2, 3, 4])
+  ==> 1 + (2 + (foldr (+) 0 [3, 4]))
+  ==> 1 + (2 + (3 + (foldr (+) 0 [4])))
+  ==> 1 + (2 + (3 + (4 + (foldr (+) 0 []))))
+  ==> 1 + (2 + (3 + (4 + 0)))
+










+

QUIZ

+

What does this evaluate to?

+
foldr f b []     = b
+foldr f b (x:xs) = f x (foldr f b xs)
+
+quiz = foldr (\x v -> x : v) [] [1,2,3]
+

(A) Type error

+

(B) [1,2,3]

+

(C) [3,2,1]

+

(D) [[3],[2],[1]]

+

(E) [[1],[2],[3]]

+










+
foldr (:) [] [1,2,3]
+  ==> (:) 1 (foldr (:) [] [2, 3])
+  ==> (:) 1 ((:) 2 (foldr (:) [] [3]))
+  ==> (:) 1 ((:) 2 ((:) 3 (foldr (:) [] [])))
+  ==> (:) 1 ((:) 2 ((:) 3 []))
+  ==  1 : (2 : (3 : []))
+  ==  [1,2,3]
+










+

QUIZ

+

What is the most general type of foldr?

+

+foldr :: (a -> b -> b) -> b -> [a] -> b 
+foldr f b []     = b
+foldr f b (x:xs) = f x (foldr f b xs)
+

(A) (a -> a -> a) -> a -> [a] -> a

+

(B) (a -> a -> b) -> a -> [a] -> b

+

(C) (a -> b -> a) -> b -> [a] -> b

+

(D) (a -> b -> b) -> b -> [a] -> b

+

(E) (b -> a -> b) -> b -> [a] -> b

+


+










+

Tail Recursive Fold

+
foldr f b []     = b
+foldr f b (x:xs) = f x (foldr f b xs)
+

Is foldr tail recursive?

+










+

What about tail-recursive versions?

+

Let’s write tail-recursive sum!

+
sumTR :: [Int] -> Int
+sumTR = ...
+










+

Lets run sumTR to see how it works

+
sumTR [1,2,3]
+  ==> helper 0 [1,2,3]
+  ==> helper 1   [2,3]    -- 0 + 1 ==> 1
+  ==> helper 3     [3]    -- 1 + 2 ==> 3
+  ==> helper 6      []    -- 3 + 3 ==> 6 
+  ==> 6
+

Note: helper directly returns the result of recursive call!

+










+

Let’s write tail-recursive cat!

+
catTR :: [String] -> String 
+catTR = ...
+










+

Lets run catTR to see how it works

+
catTR                 ["carne", "asada", "torta"]
+
+  ==> helper ""       ["carne", "asada", "torta"]
+
+  ==> helper "carne"           ["asada", "torta"]
+
+  ==> helper "carneasada"               ["torta"]
+
+  ==> helper "carneasadatorta"                 []
+
+  ==> "carneasadatorta"
+

Note: helper directly returns the result of recursive call!

+










+

Can you spot the pattern?

+
-- sumTR
+foo xs                = helper 0 xs
+  where
+    helper acc []     = acc
+    helper acc (x:xs) = helper (acc + x) xs
+
+
+-- catTR
+foo xs                = helper "" xs
+  where
+    helper acc []     = acc
+    helper acc (x:xs) = helper (acc ++ x) xs
+


+
pattern = ...
+










+

The “fold-left” pattern

+
+
The foldl Pattern
+
+

General Pattern

+
    +
  • Use a helper function with an extra accumulator argument
  • +
  • To compute new accumulator, combine current accumulator with the head using some binary operation
  • +
+





+
foldl f b xs          = helper b xs
+  where
+    helper acc []     = acc
+    helper acc (x:xs) = helper (f acc x) xs
+



+

Let’s refactor sumTR and catTR:

+
sumTR = foldl ...  ...
+
+catTR = foldl ...  ...
+

Factor the tail-recursion out!

+









+

QUIZ

+

What does this evaluate to?

+
foldl f b xs          = helper b xs
+  where
+    helper acc []     = acc
+    helper acc (x:xs) = helper (f acc x) xs
+
+quiz = foldl (\xs x -> x : xs) [] [1,2,3]
+


+

(A) Type error

+

(B) [1,2,3]

+

(C) [3,2,1]

+

(D) [[3],[2],[1]]

+

(E) [[1],[2],[3]]

+


+
foldl f b (x1: x2: x3 : [])
+  ==> helper b (x1: x2: x3 : [])
+  ==> helper (f x1 b)  (x2: x3 : [])
+  ==> helper (f x2 (f x1 b))  (x3 : [])
+  ==> helper (f x3 (f x2 (f x1 b)))  []
+  ==> ( x3 :  (x2 : (x1 : [])))
+









+

The “fold-left” pattern

+
foldl f b                     [x1, x2, x3, x4]
+  ==> helper b                [x1, x2, x3, x4]
+  ==> helper (f b x1)             [x2, x3, x4]
+  ==> helper (f (f b x1) x2)          [x3, x4]
+  ==> helper (f (f (f b x1) x2) x3)       [x4]
+  ==> helper (f (f (f (f b x1) x2) x3) x4)  []
+  ==> (f (f (f (f b x1) x2) x3) x4)
+

Accumulate the values from the left

+

For example:

+
foldl (+) 0                   [1, 2, 3, 4]
+  ==> helper 0                [1, 2, 3, 4]
+  ==> helper (0 + 1)             [2, 3, 4]
+  ==> helper ((0 + 1) + 2)          [3, 4]
+  ==> helper (((0 + 1) + 2) + 3)       [4]
+  ==> helper ((((0 + 1) + 2) + 3) + 4)  []
+  ==> ((((0 + 1) + 2) + 3) + 4)
+










+

Left vs. Right

+
foldl f b [x1, x2, x3]  ==> f (f (f b x1) x2) x3  -- Left
+
+foldr f b [x1, x2, x3]  ==> f x1 (f x2 (f x3 b))  -- Right
+

For example:

+
foldl (+) 0 [1, 2, 3]  ==> ((0 + 1) + 2) + 3  -- Left
+
+foldr (+) 0 [1, 2, 3]  ==> 1 + (2 + (3 + 0))  -- Right
+

Different types!

+
foldl :: (b -> a -> b) -> b -> [a] -> b  -- Left
+
+foldr :: (a -> b -> b) -> b -> [a] -> b  -- Right
+










+

Higher Order Functions

+

Iteration patterns over collections:

+
    +
  • Filter values in a collection given a predicate
  • +
  • Map (iterate) a given transformation over a collection
  • +
  • Fold (reduce) a collection into a value, given a binary operation to combine results
  • +
+


+

HOFs can be put into libraries to enable modularity

+
    +
  • Data structure library implements map, filter, fold for its collections

    +
      +
    • generic efficient implementation

    • +
    • generic optimizations: map f (map g xs) --> map (f.g) xs

    • +
  • +
  • Data structure clients use HOFs with specific operations

    +
      +
    • no need to know the implementation of the collection
    • +
  • +
+

Crucial foundation of

+
    +
  • “big data” revolution e.g. MapReduce, Spark, TensorFlow

  • +
  • “web programming” revolution e.g. Jquery, Angular, React

  • +
+












+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/lectures/05-closure.html b/docs/lectures/05-closure.html new file mode 100644 index 0000000..e33464c --- /dev/null +++ b/docs/lectures/05-closure.html @@ -0,0 +1,1438 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

Closures

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

Past three weeks

+

How to use essential language constructs?

+
    +
  • Data Types
  • +
  • Recursion
  • +
  • Higher-Order Functions
  • +
+

Next two weeks

+

How to implement language constructs?

+
    +
  • Local variables and scope
  • +
  • Environments and Closures
  • +
+ +

Interpreter

+

How do we represent and evaluate a program?

+ +










+

How should we evaluate this expression?

+
   eval []
+    {let c = 42 in let cTimes = \x -> c * x in cTimes 2}
+=> eval [c:42] 
+                  {let cTimes = \x -> c * x in cTimes 2}
+=> eval [cTimes:???, c:42] 
+                                              {cTimes 2}
+


+

What is the value of cTimes???

+










+

Rethinking our values

+

Until now: a program evaluates to an integer (or fails)

+
type Value = Int
+
+type Env = [(Id, Value)]
+
+eval :: Env -> Expr -> Value
+



+

What do these programs evaluate to?

+
(1)
+\x -> 2 * x
+==> ???
+
+(2)
+let f = \x -> \y -> 2 * (x + y) in
+f 5
+==> ???
+







+

Now: a program evaluates to an integer or a lambda abstraction (or fails)

+
    +
  • Remember: functions are first-class values
  • +
+


+

Let’s change our definition of values!

+
data Value = VNum Int
+           | VLam ??? -- What info do we need to store?
+
+-- Other types stay the same
+type Env = [(Id, Value)]
+
+eval :: Env -> Expr -> Value
+










+

Function values

+

How should we represent a function value?

+
let c = 42 in
+let cTimes = \x -> c * x in 
+cTimes 2
+

We need to store enough information about cTimes so that we can later evaluate any application of cTimes (like cTimes 2)!

+



+

First attempt:

+
data Value = VNum Int
+           | VLam Id Expr -- formal + body
+



+

Let’s try this!

+
   eval []         
+    {let c = 42 in let cTimes = \x -> c * x in cTimes 2}
+=> eval [c:42] 
+                  {let cTimes = \x -> c * x in cTimes 2}
+=> eval [cTimes:(\x -> c*x), c:42] 
+                                              {cTimes 2}
+    -- evaluate the function:
+=> eval [cTimes:(\x -> c*x), c:42]
+                                       {(\x -> c * x) 2} 
+    -- evaluate the argument, bind to x, evaluate body:
+=> eval [x:2, cTimes:(\x -> c*x), c:42] 
+                                              {c * x}
+=>                                            42 * 2
+=>                                            84
+


+

Looks good… can you spot a problem?

+










+

QUIZ

+

What should this evaluate to?

+
let c = 42 in
+let cTimes = \x -> c * x in -- but which c???
+let c = 5 in
+cTimes 2
+

(A) 84

+

(B) 10

+

(C) Error: multiple definitions of c

+


+









+

Static vs Dynamic Scoping

+

What we want:

+
let c = 42 in
+let cTimes = \x -> c * x in
+let c = 5 in
+cTimes 2
+
+=> 84
+

Lexical (or static) scoping:

+
    +
  • each occurrence of a variable refers to the most recent binding in the program text
  • +
  • definition of each variable is unique and known statically
  • +
  • good for readability and debugging: don’t have to figure out where a variable got “assigned”
  • +
+



+

What we don’t want:

+
let c = 42 in
+let cTimes = \x -> c * x in
+let c = 5 in
+cTimes 2
+
+=> 10
+

Dynamic scoping:

+
    +
  • each occurrence of a variable refers to the most recent binding during program execution
  • +
  • can’t tell where a variable is defined just by looking at the function body
  • +
  • nightmare for readability and debugging:
  • +
+
let cTimes = \x -> c * x in
+let c = 5 in
+let res1 = cTimes 2 in -- ==> 10
+let c = 10 in
+let res2 = cTimes 2 in -- ==> 20!!!
+res2 - res1
+










+

Function values

+
data Value = VNum Int
+           | VLam Id Expr -- formal + body
+

This representation can only implement dynamic scoping!

+
let c = 42 in
+let cTimes = \x -> c * x in
+let c = 5 in
+cTimes 2
+

evaluates as:

+
   eval []         
+   {let c = 42 in let cTimes = \x -> c * x in let c = 5 in cTimes 2}
+=> eval [c:42] 
+                 {let cTimes = \x -> c * x in let c = 5 in cTimes 2}
+=> eval [cTimes:(\x -> c*x), c:42] 
+                                             {let c = 5 in cTimes 2}
+=> eval [c:5, cTimes:(\x -> c*x), c:42] 
+                                                          {cTimes 2}
+=> eval [c:5, cTimes:(\x -> c*x), c:42]
+                                                   {(\x -> c * x) 2} 
+=> eval [x:2, c:5, cTimes:(\x -> c*x), c:42] 
+                                                          {c * x}
+  -- latest binding for c is 5!
+=>                                                         5 * 2
+=>                                                         10
+

Lesson learned: need to remember what c was bound to when cTimes was defined!

+
    +
  • i.e. “freeze” the environment at function definition
  • +
+










+

Closures

+

To implement lexical scoping, we will represent function values as closures

+


+

Closure = lambda abstraction (formal + body) + environment at function definition

+


+
data Value = VNum Int
+           | VClos Env Id Expr -- env + formal + body
+


+

Our example:

+
   eval []         
+   {let c = 42 in let cTimes = \x -> c * x in let c = 5 in cTimes 2}
+=> eval [c:42] 
+                 {let cTimes = \x -> c * x in let c = 5 in cTimes 2}
+   -- remember current env:
+=> eval [cTimes:<[c:42], \x -> c*x>, c:42] 
+                                             {let c = 5 in cTimes 2}
+=> eval [c:5, cTimes:<[c:42], \x -> c*x>, c:42] 
+                                                          {cTimes 2}
+=> eval [c:5, cTimes:<[c:42], \x -> c*x>, c:42]
+                                           {<[c:42], \x -> c * x> 2}
+  -- restore env to the one inside the closure, then bind 2 to x:                                                 
+=> eval [x:2, c:42] 
+                                                          {c * x}
+=>                                                        42 * 2
+=>                                                        84
+










+

QUIZ

+

Which variables should be saved in the closure environment of f?

+
let a = 20 in
+let f = 
+  \x -> let y = x + 1 in
+        let g = \z -> y + z in
+        a + g x 
+  in ...        
+

(A) a

+

(B) a x

+

(C) y g

+

(D) a y g

+

(E) a x y g z

+


+









+

Free vs bound variables

+
    +
  • An occurrence of x is free if it is not bound
  • +
  • An occurrence of x is bound if it’s inside +
      +
    • e2 where let x = e1 in e2
    • +
    • e where \x -> e
    • +
  • +
  • A closure environment has to save all free variables of a function definition!
  • +
+
let a = 20 in
+let f = 
+  \x -> let y = x + 1 in
+        let g = \z -> y + z in
+        a + g x -- a is the only free variable!
+  in ...        
+










+

Evaluator

+

Let’s modify our evaluator to handle functions!

+
data Value = VNum Int
+           | VClos Env Id Expr -- env + formal + body
+           
+eval :: Env -> Expr -> Value
+eval env (Num n)        = VNum n -- must wrap in VNum now!
+eval env (Var x)        = lookup x env
+eval env (Bin op e1 e2) = VNum (f v1 v2)
+  where
+    (VNum v1) = eval env e1
+    (VNum v2) = eval env e2
+    f = ... -- as before
+eval env (Let x e1 e2) = eval env' e2
+  where
+    v = eval env e1
+    env' = add x v env
+eval env (Lam x body) = ??? -- construct a closure
+eval env (App fun arg) = ??? -- eval fun, then arg, then apply
+










+

Evaluating functions:

+
    +
  • Construct a closure: save environment at function definition
  • +
  • Apply a closure: restore saved environment, add formal, evaluate the body
  • +
+
eval :: Env -> Expr -> Value
+...
+eval env (Lam x body) = VClos env x body
+eval env (App fun arg) = eval bodyEnv body
+  where
+    (VClos closEnv x body) = eval env fun -- eval function to closure
+    vArg                   = eval env arg -- eval argument
+    bodyEnv                = add x vArg closEnv
+

QUIZ

+

With eval as defined above, what does this evaluate to?

+
let f = \x -> x + y in
+let y = 10 in
+f 5
+

(A) 15

+

(B) 5

+

(C) Error: unbound variable x

+

(D) Error: unbound variable y

+

(E) Error: unbound variable f

+


+








+
   eval []         
+     {let f = \x -> x + y in let y = 10 in f 5}
+=> eval [f:<[], \x -> x + y>]
+                            {let y = 10 in f 5}
+=> eval [y:10, f:<[], \x -> x + y>]
+                                          {f 5}
+=> eval [y:10, f:<[], \x -> x + y>]
+                          {<[], \x -> x + y> 5}  
+=> eval [x:5] -- env got replaced by closure env + formal!
+                                     {x + y}  -- y is unbound!
+









+

QUIZ

+

With eval as defined above, what does this evaluate to?

+
let f = \n -> n * f (n - 1) in
+f 5
+

(A) 120

+

(B) Evaluation does not terminate

+

(C) Error: unbound variable f

+


+








+
   eval []         
+       {let f = \n -> n * f (n - 1) in f 5}
+=> eval [f:<[], \n -> n * f (n - 1)>]
+                                      {f 5}
+=> eval [f:<[], \n -> n * f (n - 1)>]
+              {<[], \n -> n * f (n - 1)> 5}   
+=> eval [n:5] -- env got replaced by closure env + formal!
+                         {n * f (n - 1)} -- f is unbound!
+

Lesson learned: to support recursion, we need a different way of constructing the closure environment!

+










+

The Nano Language

+

Features of Nano:

+
    +
  1. Arithmetic expressions [done]
  2. +
  3. Variables and let-bindings [done]
  4. +
  5. Functions [done]
  6. +
  7. Recursion [this is part of HW4]
  8. +
+









+ +
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/lectures/05-environments.html b/docs/lectures/05-environments.html new file mode 100644 index 0000000..2d336ad --- /dev/null +++ b/docs/lectures/05-environments.html @@ -0,0 +1,1072 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

Environments

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

Past three weeks

+

How to use essential language constructs?

+
    +
  • Data Types
  • +
  • Recursion
  • +
  • Higher-Order Functions
  • +
+

Next two weeks

+

How to implement language constructs?

+
    +
  • Local variables and scope

  • +
  • Environments and Closures

  • +
  • (skip) Type Inference

  • +
+

Interpreter

+

How do we represent and evaluate a program?

+ +










+

Roadmap: The Nano Language

+

Features of Nano:

+
    +
  1. Arithmetic
  2. +
  3. Variables
  4. +
  5. Let-bindings
  6. +
  7. Functions
  8. +
  9. Recursion
  10. +
+

+










+

1. Nano: Arithmetic

+

A grammar of arithmetic expressions:

+
e ::= n
+    | e1 + e2
+    | e1 - e2
+    | e1 * e2
+


+ + + + + + + + + + + + + + + + + + + + + + + + + +
ExpressionsValues
4==>4
4 + 12==>16
(4+12) - 5==>11
+



+

Representing Arithmetic Expressions and Values

+

+

Lets represent arithmetic expressions as type

+
data Expr
+  = ENum Int         -- ^ n
+  | EAdd Expr Expr   -- ^ e1 + e2
+  | ESub Expr Expr   -- ^ e1 - e2
+  | EMul Expr Expr   -- ^ e1 * e2
+

Lets represent arithmetic values as a type

+
type Value = Int
+





+

Evaluating Arithmetic Expressions

+

+

We can now write a Haskell function to evaluate an expression:

+
eval :: Expr -> Value
+eval (ENum n)     = n
+eval (EAdd e1 e2) = eval e1 + eval e2
+eval (ESub e1 e2) = eval e1 - eval e2
+eval (EMul e1 e2) = eval e1 * eval e2
+









+

Alternative representation

+

Lets pull the operators into a separate type

+
data Binop = Add                  -- ^ `+`
+           | Sub                  -- ^ `-`
+           | Mul                  -- ^ `*`
+
+data Expr  = ENum Int              -- ^ n
+           | EBin Binop Expr Expr  -- ^ e1 `op` e2
+









+

QUIZ

+

Evaluator for alternative representation

+
eval :: Expr -> Value
+eval (ENum n)        = n
+eval (EBin op e1 e2) = evalOp op (eval e1) (eval e2)
+

What is a suitable type for evalOp?

+
{- 1 -} evalOp :: BinOp -> Value
+{- 2 -} evalOp :: BinOp -> Value -> Value -> Value
+{- 3 -} evalOp :: BinOp -> Expr  -> Expr -> Value
+{- 4 -} evalOp :: BinOp -> Expr -> Expr -> Expr
+{- 5 -} evalOp :: BinOp -> Expr -> Value
+










+

The Nano Language

+

Features of Nano:

+
    +
  1. Arithmetic [done]
  2. +
  3. Variables
  4. +
  5. Let-bindings
  6. +
  7. Functions
  8. +
  9. Recursion
  10. +
+

+










+

2. Nano: Variables

+

Let’s add variables and let bindings!

+
e ::= n                   -- OLD
+    | e1 + e2
+    | e1 - e2
+    | e1 * e2
+                          -- NEW
+    | x                   -- variables
+

Lets extend our datatype

+
type Id = String
+
+data Expr
+  = ENum Int             -- OLD
+  | EBin Binop Expr Expr  
+                         -- NEW
+  | EVar Id              -- variables
+


+

QUIZ

+

What should the following expression evaluate to?

+
x + 1
+

(A) 0

+

(B) 1

+

(C) Error

+










+

Environment

+

An expression is evaluated in an environment

+
    +
  • A phone book which maps variables to values
  • +
+
[ "x" := 0, "y" := 12, ...]
+

A type for environments

+
type Env = [(Id, Value)]
+






+

Evaluation in an Environment

+

We write

+
(eval env expr) ==> value
+

to mean

+

When expr is evaluated in environment env the result is value

+

So: when we have variables, we modify our evaluator (eval)

+
    +
  • to take an input environment env in which expr must be evaluated.
  • +
+
eval :: Env -> Expr -> Value
+eval env expr = -- ... value-of-expr-in-env...
+

First, lets update the evaluator for the arithmetic cases ENum and EBin

+
eval :: Env -> Expr -> Value
+eval env (ENum n)        = ???
+eval env (EBin op e1 e2) = ???
+











+

QUIZ

+

What is a suitable ?value such that

+
eval [ "x" := 0, "y" := 12, ...] (x + 1)  ==>  ?value
+

(A) 0

+

(B) 1

+

(C) Error

+



















+

QUIZ

+

What is a suitable env such that

+
eval env (x + 1)   ==>   10
+

(A) []

+

(B) [x := 0, y := 9]

+

(C) [x := 9, y := 0]

+

(D) [x := 9, y := 10, z := 666]

+

(E) [y := 10, z := 666, x := 9]

+



















+

Evaluating Variables

+

Using the above intuition, lets update our evaluator to handle variables i.e. the EVar case:

+
eval env (EVar x)        = ???
+



+

Lets confirm that our eval is ok!

+
envA = []
+envB = ["x" := 0 , "y" := 9]
+envC = ["x" := 9 , "y" := 0]
+envD = ["x" := 9 , "y" := 10 , "z" := 666]
+envE = ["y" := 10, "z" := 666, "x" := 9  ]
+
+-- >>> eval envA (EBin Add (EVar "x") (ENum 1))
+-- >>> eval envB (EBin Add (EVar "x") (ENum 1))
+-- >>> eval envC (EBin Add (EVar "x") (ENum 1))
+-- >>> eval envD (EBin Add (EVar "x") (ENum 1))
+-- >>> eval envE (EBin Add (EVar "x") (ENum 1))
+



















+

The Nano Language

+

Features of Nano:

+
    +
  1. Arithmetic expressions [done]
  2. +
  3. Variables [done]
  4. +
  5. Let-bindings
  6. +
  7. Functions
  8. +
  9. Recursion
  10. +
+

+










+

2. Nano: Variables

+

Let’s add variables and let bindings!

+
e ::= n                   -- OLD
+    | e1 + e2
+    | e1 - e2
+    | e1 * e2
+    | x
+                          -- NEW
+    | let x = e1 in e2
+

Lets extend our datatype

+
type Id = String
+
+data Expr
+  = ENum Int              -- OLD
+  | EBin Binop Expr Expr  
+  | EVar Id
+                         -- NEW
+  | ELet Id Expr Expr
+

How should we extend eval ?

+




















+

QUIZ

+

What should the following expression evaluate to?

+
let x = 0
+in
+  x + 1
+

(A) Error

+

(B) 1

+

(C) 0

+










+

QUIZ

+

What should the following expression evaluate to?

+
let x = 0
+in
+  let y = 100
+  in
+    x + y
+

(A) Error

+

(B) 0

+

(C) 1

+

(D) 100

+

(E) 101

+










+

QUIZ

+

What should the following expression evaluate to?

+
let x = 0
+in
+  let x = 100
+  in
+    x + 1
+

(A) Error

+

(B) 0

+

(C) 1

+

(D) 100

+

(E) 101

+










+

QUIZ

+

What should the following expression evaluate to?

+
let x = 0
+in
+  (let x = 100 in
+   in
+     x + 1
+  )
+  +
+  x
+

(A) Error

+

(B) 1

+

(C) 101

+

(D) 102

+

(E) 2

+










+

Principle: Static/Lexical Scoping

+

Every variable use gets its value from a unique definition:

+
    +
  • “Nearest” let-binder in program text
  • +
+

Static means you can tell without running the program

+

Great for readability and debugging

+
    +
  1. Define local variables

  2. +
  3. Be sure where each variable got its value

  4. +
+

Don’t have to scratch head to figure where a variable got “assigned”

+

How to implement static scoping?

+













+

QUIZ

+

Lets re-evaluate the quizzes!

+
              -- env
+let x = 0
+in            -- ??? what env to use for `x + 1`?
+  x + 1
+

(A) env

+

(B) [ ]

+

(C) [ ("x" := 0) ]

+

(D) ("x" := 0) : env

+

(E) env ++ ["x" := 0]

+














+

QUIZ

+
                  -- env
+let x = 0
+in                  -- (x := 0) : env
+  let y = 100
+  in                  -- ??? what env to use for `x + y` ?
+    x + y
+

(A) ("x" := 0) : env

+

(B) ("y" := 100) : env

+

(C) ("y" := 100) : ("x" := 0) : env

+

(D) ("x" := 0) : ("y" := 100) : env

+

(E) [("y" := 100), ("x" := 0)]

+















+

QUIZ

+

Lets re-evaluate the quizzes!

+
              -- env
+let x = 0
+in              -- ("x" := 0) : env
+  let x = 100
+  in              -- ??? what env to use for `x + 1`?
+    x + 1
+

(A) ("x" := 0) : env

+

(B) ("x" := 100) : env

+

(C) ("x" := 100) : ("x" := 0) : env

+

(D) ("x" := 0) : ("x" := 100) : env

+

(E) [("x" := 100)]

+



















+

Extending Environments

+

Lets fill in eval for the let x = e1 in e2 case!

+
eval env (ELet x e1 e2) = ???
+
    +
  1. Evaluate e1 in env to get a value v1

  2. +
  3. Extend environment with value for x i.e. to (x := v1) : env

  4. +
  5. Evaluate e2 using extended environment.

  6. +
+
















+

Lets make sure our tests pass!

+

Run-time Errors

+

Haskell function to evaluate an expression:

+
eval :: Env -> Expr -> Value
+eval env (Num n)        = n
+eval env (Var x)        = lookup x env      -- (A)
+eval env (Bin op e1 e2) = evalOp op v1 v2   -- (B)
+  where
+    v1                  = eval env  e1      -- (C)
+    v2                  = eval env  e2      -- (C) 
+eval env (Let x e1 e2)  = eval env1 e2
+  where
+    v1                  = eval env e1
+    env1                = (x, v1) : env     -- (D)
+

QUIZ

+

Will eval env expr always return a value ? Or, can it crash?

+

(A) operation at A may fail (B) operation at B may fail (C) operation at C may fail (D) operation at D may fail (E) nah, its all good…, always returns a Value

+











+

Free vs bound variables

+

Undefined Variables

+

How do we make sure lookup doesn’t cause a run-time error?

+

Bound Variables

+

Consider an expression let x = e1 in e2

+
    +
  • An occurrence of x is bound in e2

  • +
  • i.e. when occurrence of form let x = ... in ... x ...

  • +
  • i.e. when x occurs “under” a let binding for x.

  • +
+

Free Variables

+

An occurrence of x is free in e if it is not bound in e

+

Closed Expressions

+

An expression e is closed in environment env:

+
    +
  • If all free variables of e are defined in env
  • +
+

Successful Evaluation

+

lookup will never fail

+
    +
  • If eval env e is only called on e that is closed in env
  • +
+















+

QUIZ

+

Which variables occur free in the expression?

+
let y = (let x = 2
+         in x      ) + x
+in
+  let x = 3
+  in
+    x + y
+

(A) None

+

(B) x

+

(C) y

+

(D) x and y

+


+










+

Exercise to try at home

+

Consider the function

+
evaluate :: Expr -> Value
+evaluate e
+  | isOk e    = eval emptyEnv e
+  | otherwise = error "Sorry! bad expression, it will crash `eval`!"  
+  where
+    emptyEnv  = []               -- has NO bindings
+

What should isOk check for? (Try to implement it for nano…)

+
















+

The Nano Language

+

Features of Nano:

+
    +
  1. Arithmetic expressions [done]
  2. +
  3. Variables [done]
  4. +
  5. Let-bindings [done]
  6. +
  7. Functions
  8. +
  9. Recursion
  10. +
+

+















+

Nano: Functions

+

Let’s add

+
    +
  • lambda abstraction (aka function definitions)

  • +
  • application (aka function calls)

  • +
+
e ::= n                   -- OLD
+    | e1 `op` e2
+    | x
+    | let x = e1 in e2
+                          -- NEW
+    | \x -> e             -- abstraction
+    | e1 e2               -- application
+

Example

+
let incr = \x -> x + 1
+in
+   incr 10
+



















+

Representation

+
data Expr
+  = ENum Int              -- OLD
+  | EBin Binop Expr Expr  
+  | EVar Id
+  | ELet Id Expr Expr
+                         -- NEW
+  | ???                  -- abstraction \x -> e
+  | ???                  -- application (e1 e2)
+



















+

Representation

+
data Expr
+  = ENum Int              -- OLD
+  | EBin Binop Expr Expr  
+  | EVar Id
+  | ELet Id Expr Expr
+                         -- NEW
+  | ELam Id Expr         -- abstraction \x -> e
+  | EApp Expr Expr       -- application (e1 e2)
+

Example

+
let incr = \x -> x + 1
+in
+   incr 10
+

is represented as

+
ELet "incr" (ELam "x" (EBin Add (EVar "x") (ENum 1)))
+  (
+    EApp (EVar "incr") (ENum 10)
+  )
+















+

Functions are Values

+

Recall the trinity

+

+

But… what is the value of a function?

+

Lets build some intuition with examples.

+











+

QUIZ

+

What does the following expression evaluate to?

+
let incr = \x -> x + 1    -- abstraction ("definition")
+in
+   incr 10                -- application ("call")
+

(A) Error/Undefined

+

(B) 10

+

(C) 11

+

(D) 0

+

(E) 1

+











+

What is the Value of incr?

+
    +
  • Is it an Int ?

  • +
  • Is it a Bool ?

  • +
  • Is it a ???

  • +
+

What information do we need to store (in the Env) about incr?

+











+

A Function’s Value is its Code

+
                        -- env
+let incr = \x -> x + 1
+in                      -- ("incr" := <code>) : env
+    incr 10             -- evaluate <code> with parameter := 10
+

What information do we store about <code> ?

+












+

A Call’s Value

+

How to evaluate the “call” incr 10 ?

+
    +
  1. Lookup the <code> i.e. <param, body> for incr (stored in the environment),

  2. +
  3. Evaluate body with param set to 10!

  4. +
+













+

Two kinds of Values

+

We now have two kinds of Values

+
v ::= n               -- OLD
+    | <x, e>          -- <param, body>
+
    +
  1. Plain Int (as before)
  2. +
  3. A function’s “code”: a pair of “parameter” and “body-expression”
  4. +
+
data Value
+  = VInt  Int          -- OLD
+  | VCode Id Expr      -- <x, e>
+

+

Evaluating Lambdas and Applications

+
eval :: Env -> Expr -> Value
+                                -- OLD
+eval env (ENum n)        = ???
+eval env (EVar x)        = ???
+eval env (EBin op e1 e2) = ???
+eval env (ELet x  e1 e2) = ???
+                                -- NEW
+eval env (ELam x e)      = ???
+eval env (EApp e1 e2)    = ???
+

Lets make sure our tests work properly!

+
exLam1 = ELet "incr" (ELam "x" (EBin Add (EVar "x") (ENum 1)))
+          (
+            EApp (EVar "incr") (ENum 10)
+          )
+
+-- >>> eval [] exLam1
+-- 11
+

QUIZ

+

What should the following evaluate to?

+
let c = 1
+in
+   let inc = \x -> x + c
+   in
+      inc 10
+

(A) Error/Undefined

+

(B) 10

+

(C) 11

+

(D) 0

+

(E) 1

+









+
exLam2 = ELet "c" (ENum 1)
+           (ELet "incr" (ELam "x" (EBin Add (EVar "x") (EVar "c")))
+              (
+              EApp (EVar "incr") (ENum 10)
+              )
+           )
+
+-- >>> eval [] exLam2
+-- ???
+

QUIZ

+

And what should this expression evaluate to?

+
let c = 1
+in
+   let inc = \x -> x + c
+   in
+      let c = 100
+      in
+        inc 10
+

(A) Error/Undefined

+

(B) 110

+

(C) 11

+









+

The “Immutability Principle”

+

A function’s behavior should never change

+
    +
  • A function must always return the same output for a given input
  • +
+

Why?

+
> myFunc 10
+0
+
+> myFunc 10
+10
+

Oh no! How to find the bug? Is it

+
    +
  • In myFunc or
  • +
  • In a global variable or
  • +
  • In a library somewhere else or
  • +
  • +
+

My worst debugging nightmare

+

Colbert “Immutability Principle”

+ +











+

The Immutability Principle ?

+

How does our eval work?

+
exLam3 = ELet "c" (ENum 1)
+           (
+           ELet "incr" (ELam "x" (EBin Add (EVar "x") (EVar "c")))
+              (
+               ELet "c" (ENum 100)
+                 (
+                 EApp (EVar "incr") (ENum 10)
+                 )
+              )
+           )
+
+-- >>> eval [] exLam3
+-- ???
+

Oops?

+
                          -- []
+let c = 1
+in                        -- ["c" := 1]
+   let inc = \x -> x + c
+   in                     -- ["inc" := <x, x+c>, c := 1]
+      let c = 100
+      in                  -- ["c" := 100, "inc" := <x, x+c", "c" := 1]   <<< env
+        inc 10
+

And so we get

+
eval env (inc 10)
+
+  ==> eval ("x" := 10 : env) (x + c)
+
+  ==> 10 + 100
+
+  ==> 110
+

Ouch.

+











+

Enforcing Immutability with Closures

+

How to enforce immutability principle

+
    +
  • inc 10 always returns 11?
  • +
+


+

Key Idea: Closures

+

At definition: Freeze the environment the function’s value

+

At call: Use the frozen environment to evaluate the body

+

Ensures that inc 10 always evaluates to the same result!

+
                          -- []
+let c = 1
+in                        -- ["c" := 1]
+   let inc = \x -> x + c
+   in                     -- ["inc" := <frozenv, x, x+c>, c := 1]  <<< frozenv = ["c" := 1]
+      let c = 100
+      in                  -- ["c" := 100, "inc" := <frozenv, x, x+c>, "c" := 1]
+        inc 10
+

Now we evaluate

+
eval env (inc 10)
+
+  ==> eval ("x" := 10 : frozenv) (x + c)  where frozenv = ["c" := 1]
+
+  ==> 10 + 1
+
+  ==> 1
+

tada!

+











+

Representing Closures

+

Lets change the Value datatype to also store an Env

+
data Value
+  = VInt  Int          -- OLD
+  | VClos Env Id Expr  -- <frozenv, param, body>  
+











+

Evaluating Function Definitions

+

How should we fix the definition of eval for ELam?

+
eval :: Env -> Expr -> Value
+
+eval env (ELam x e) = ???
+

Hint: What value should we bind incr to in our example above?

+


+

(Recall At definition freeze the environment the function’s value)

+


+

Evaluating Function Calls

+

How should we fix the definition of eval for EApp?

+
eval :: Env -> Expr -> Value
+
+eval env (EApp e1 e2) = ???
+


+

(Recall At call: Use the frozen environment to evaluate the body)

+


+

Hint: What value should we evaluate incr 10 to?

+
    +
  1. Evaluate incr to get <frozenv, "x", x + c>
  2. +
  3. Evaluate 10 to get 10
  4. +
  5. Evaluate x + c in x:=10 : frozenv
  6. +
+


+

Let’s generalize that recipe!

+
    +
  1. Evaluate e1 to get <frozenv, param, body>
  2. +
  3. Evaluate e2 to get v2
  4. +
  5. Evaluate body in param := v2 : frozenv
  6. +
+











+

Immutability Achieved

+

Lets put our code to the test!

+
exLam3 = ELet "c" (ENum 1)
+           (
+           ELet "incr" (ELam "x" (EBin Add (EVar "x") (EVar "c")))
+              (
+               ELet "c" (ENum 100)
+                 (
+                 EApp (EVar "incr") (ENum 10)
+                 )
+              )
+           )
+
+-- >>> eval [] exLam3
+-- ???
+











+

QUIZ

+

What should the following evaluate to?

+
let add = \x -> (\y -> x + y)
+in
+  let add10 = add 10
+  in
+    let add20 = add 20
+    in 
+      (add10 100) + (add20 1000)
+

A. 1100

+

B. 1110

+

C. 1120

+

D. 1130

+

E. 1140

+











+

Functions Returning Functions Achieved!

+
exLam4 = ...
+
+-- >>> eval [] exLam4
+











+

Practice

+

What should the following evaluate to?

+
let add = \x -> (\y -> x + y)
+in
+  let add10 = add 10
+  in
+    let doTwice = \f -> (\x -> f (f x))
+    in
+      doTwice add10 100
+











+

Functions Accepting Functions Achieved!

+
exLam5 = ...
+
+-- >>> eval [] exLam4
+











+

The Nano Language

+

+

Features of Nano:

+
    +
  1. Arithmetic expressions [done]
  2. +
  3. Variables [done]
  4. +
  5. Let-bindings [done]
  6. +
  7. Functions [done]
  8. +
  9. Recursion
  10. +
+

… You figure it out Hw4 … :-)

+








+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/lectures/06-parsing.html b/docs/lectures/06-parsing.html new file mode 100644 index 0000000..ddb148b --- /dev/null +++ b/docs/lectures/06-parsing.html @@ -0,0 +1,696 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

Lexing and Parsing

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

Plan for this week

+

Last week:

+
    +
  • How do we evaluate a program given its AST?
  • +
+
eval :: Env -> Expr -> Value
+

This week:

+
    +
  • How do we convert program text into an AST?
  • +
+
parse :: String -> Expr
+










+

Example: calculator with variables

+

AST representation:

+
data Aexpr 
+  = AConst  Int
+  | AVar    Id
+  | APlus   Aexpr Aexpr
+  | AMinus  Aexpr Aexpr
+  | AMul    Aexpr Aexpr
+  | ADiv    Aexpr Aexpr    
+


+

Evaluator:

+
eval :: Env -> Aexpr -> Value
+...
+


+

Using the evaluator:

+
λ> eval [] (APlus (AConst 2) (AConst 6))
+8
+
+λ> eval [("x", 16), ("y", 10)] (AMinus (AVar "x") (AVar "y"))
+6
+
+λ> eval [("x", 16), ("y", 10)] (AMinus (AVar "x") (AVar "z"))
+*** Exception: Error {errMsg = "Unbound variable z"}
+


+

But writing ASTs explicitly is really tedious, we are used to writing programs as text!

+


+

We want to write a function that converts strings to ASTs if possible:

+
parse :: String -> Aexpr
+


+

For example:

+
λ> parse "2 + 6"
+APlus (AConst 2) (AConst 6)
+
+λ> parse "(x - y) / 2"
+ADiv (AMinus (AVar "x") (AVar "y")) (AConst 2)
+
+λ> parse "2 +"
+*** Exception: Error {errMsg = "Syntax error"}
+










+

Two-step-strategy

+

How do I read a sentence “He ate a bagel”?

+
    +
  • First split into words: ["He", "ate", "a", "bagel"]
  • +
  • Then relate words to each other: “He” is the subject, “ate” is the verb, etc
  • +
+


+

Let’s do the same thing to “read” programs!

+





+

Step 1 (Lexing) : From String to Tokens

+

A string is a list of characters:

+
+
Characters
+
+

First we aggregate characters that “belong together” into tokens (i.e. the “words” of the program):

+
+
Tokens
+
+

We distinguish tokens of different kinds based on their format:

+
    +
  • all numbers: integer constant
  • +
  • alphanumeric, starts with a letter: identifier
  • +
  • +: plus operator
  • +
  • etc
  • +
+





+

Step 2 (Parsing) : From Tokens to AST

+

Next, we convert a sequence of tokens into an AST

+
    +
  • This is hard…
  • +
  • … but the hard parts do not depend on the language!
  • +
+


+

Parser generators

+
    +
  • Given the description of the token format generates a lexer
  • +
  • Given the description of the grammar generates a parser
  • +
+

We will be using parser generators, so we only care about how to describe the token format and the grammar

+










+

Lexing

+

We will use the tool called alex to generate the lexer

+

Input to alex: a .x file that describes the token format

+










+

Tokens

+

First we list the kinds of tokens we have in the language:

+
data Token
+  = NUM    AlexPosn Int
+  | ID     AlexPosn String
+  | PLUS   AlexPosn
+  | MINUS  AlexPosn
+  | MUL    AlexPosn
+  | DIV    AlexPosn
+  | LPAREN AlexPosn
+  | RPAREN AlexPosn
+  | EOF    AlexPosn
+










+

Token rules

+

Next we describe the format of each kind of token using a rule:

+
  [\+]                          { \p _ -> PLUS   p }
+  [\-]                          { \p _ -> MINUS  p }
+  [\*]                          { \p _ -> MUL    p }
+  [\/]                          { \p _ -> DIV    p }
+  \(                            { \p _ -> LPAREN p }
+  \)                            { \p _ -> RPAREN p }
+  $alpha [$alpha $digit \_ \']* { \p s -> ID     p s }
+  $digit+                       { \p s -> NUM p (read s) }
+

Each line consist of:

+
    +
  • a regular expression that describes which strings should be recognized as this token
  • +
  • a Haskell expression that generates the token
  • +
+

You read it as:

+
    +
  • if at position p in the input string
  • +
  • you encounter a substring s that matches the regular expression
  • +
  • evaluate the Haskell expression with arguments p and s
  • +
+










+

Regular Expressions

+

A regular expression has one of the following forms:

+
    +
  • [c1 c2 ... cn] matches any of the characters c1 .. cn

    +
      +
    • [0-9] matches any digit
    • +
    • [a-z] matches any lower-case letter
      +
    • +
    • [A-Z] matches any upper-case letter
    • +
    • [a-z A-Z] matches any letter
    • +
  • +
  • R1 R2 matches a string s1 ++ s2 where s1 matches R1 and s2 matches R2

    +
      +
    • e.g. [0-9] [0-9] matches any two-digit string
    • +
  • +
  • R+ matches one or more repetitions of what R matches

    +
      +
    • e.g. [0-9]+ matches a natural number
    • +
  • +
  • R* matches zero or more repetitions of what R matches

  • +
+



+

QUIZ

+

Which of the following strings are matched by [a-z A-Z] [a-z A-Z 0-9]*?

+

(A) (empty string)

+

(B) 5

+

(C) x5

+

(D) x

+

(E) C and D

+


+









+

Back to token rules

+

We can name some common regexps like:

+
$digit = [0-9]
+$alpha = [a-z A-Z]
+

and write [a-z A-Z] [a-z A-Z 0-9]* as $alpha [$alpha $digit]*

+


+
  [\+]                          { \p _ -> PLUS   p }
+  [\-]                          { \p _ -> MINUS  p }
+  [\*]                          { \p _ -> MUL    p }
+  [\/]                          { \p _ -> DIV    p }
+  \(                            { \p _ -> LPAREN p }
+  \)                            { \p _ -> RPAREN p }
+  $alpha [$alpha $digit \_ \']* { \p s -> ID     p s }
+  $digit+                       { \p s -> NUM p (read s) }
+
    +
  • When you encounter a +, generate a PLUS token
  • +
  • +
  • When you encounter a nonempty string of digits, convert it into an integer and generate a NUM
  • +
  • When you encounter an alphanumeric string that starts with a letter, save it in an `ID token
  • +
+










+

Running the Lexer

+

From the token rules, alex generates a function alexScan which

+
    +
  • given an input string, find the longest prefix p that matches one of the rules
  • +
  • if p is empty, it fails
  • +
  • otherwise, it converts p into a token and returns the rest of the string
  • +
+

We wrap this function into a handy function

+
parseTokens :: String -> Either ErrMsg [Token]
+

which repeatedly calls alexScan until it consumes the whole input string or fails

+


+

We can test the function like so:

+
λ> parseTokens "23 + 4 / off -"
+Right [ NUM (AlexPn 0 1 1) 23
+      , PLUS (AlexPn 3 1 4)
+      , NUM (AlexPn 5 1 6) 4
+      , DIV (AlexPn 7 1 8)
+      , ID (AlexPn 9 1 10) "off"
+      , MINUS (AlexPn 13 1 14) 
+      ]      
+
λ> parseTokens "%"
+Left "lexical error at 1 line, 1 column"
+





+

QUIZ

+

What is the result of parseTokens "92zoo" (positions omitted for readability)?

+

(A) Lexical error

+

(B) [ID "92zoo"]

+

(C) [NUM "92"]

+

(D) [NUM "92", ID "zoo"]

+


+









+

Parsing

+

We will use the tool called happy to generate the parser

+

Input to happy: a .y file that describes the grammar

+



+

Wait, wasn’t this the grammar?

+
data Aexpr 
+  = AConst  Int
+  | AVar    Id
+  | APlus   Aexpr Aexpr
+  | AMinus  Aexpr Aexpr
+  | AMul    Aexpr Aexpr
+  | ADiv    Aexpr Aexpr    
+

This was abstract syntax

+

Now we need to describe concrete syntax

+
    +
  • What programs look like when written as text
  • +
  • and how to map that text into the abstract syntax
  • +
+









+

Grammars

+

A grammar is a recursive definition of a set of trees

+
    +
  • each tree is a parse tree for some string
  • +
  • parse a string s = find a parse tree for s that belongs to the grammar
  • +
+



+

A grammar is made of:

+
    +
  • Terminals: the leaves of the tree (tokens!)

  • +
  • Nonterminals: the internal nodes of the tree

  • +
  • Production Rules that describe how to “produce” a non-terminal from terminals and other non-terminals

    +
      +
    • i.e. what children each nonterminal can have:
    • +
  • +
+
Aexpr :   -- NT Aexpr can have as children:
+  | Aexpr '+' Aexpr  { ... } -- NT Aexpr, T '+', and NT Aexpr, or 
+  | Aexpr '-' AExpr  { ... } -- NT Aexpr, T '-', and NT Aexpr, or 
+  | ...
+









+

Terminals

+

Terminals correspond to the tokens returned by the lexer

+

In the .y file, we have to declare with terminals in the rules correspond to which tokens from the Token datatype:

+
%token
+    TNUM  { NUM _ $$ }
+    ID    { ID _ $$  }
+    '+'   { PLUS _   }
+    '-'   { MINUS _  }
+    '*'   { MUL _    }
+    '/'   { DIV _    }
+    '('   { LPAREN _ }
+    ')'   { RPAREN _ }
+
    +
  • Each thing on the left is terminal (as appears in the production rules)

  • +
  • Each thing on the right is a Haskell pattern for datatype Token

  • +
  • We use $$ to designate one parameter of a token constructor as the value of that token

    +
      +
    • we will refer back to it from the production rules
    • +
  • +
+









+

Production rules

+

Next we define productions for our language:

+
Aexpr : TNUM                    { AConst $1    }
+      | ID                      { AVar   $1    }
+      | '(' Aexpr ')'           { $2           }
+      | Aexpr '*' Aexpr         { AMul   $1 $3 }
+      | Aexpr '+' Aexpr         { APlus  $1 $3 } 
+      | Aexpr '-' Aexpr         { AMinus $1 $3 }
+

The expression on the right computes the value of this node

+
    +
  • $1 $2 $3 refer to the values of the respective child nodes
  • +
+



+

Example: parsing (2) as AExpr:

+
    +
  1. Lexer returns a sequence of Tokens: [LPAREN, NUM 2, RPAREN]

  2. +
  3. LPAREN is the token for terminal '(', so let’s pick production '(' Aexpr ')'

  4. +
  5. Now we have to parse NUM 2 as Aexpr and RPAREN as ')'

  6. +
  7. NUM 2 is a token for nonterminal TNUM, so let’s pick production TNUM

  8. +
  9. The value of this Aexpr node is AConst 2, since the value of TNUM is 2

  10. +
  11. The value of the top-level Aexpr node is also AConst 2 (see the '(' Aexpr ')' production)

  12. +
+









+

QUIZ

+

What is the value of the root AExpr node when parsing 1 + 2 + 3?

+
Aexpr : TNUM                    { AConst $1    }
+      | ID                      { AVar   $1    }
+      | '(' Aexpr ')'           { $2           }
+      | Aexpr '*' Aexpr         { AMul   $1 $3 }
+      | Aexpr '+' Aexpr         { APlus  $1 $3 } 
+      | Aexpr '-' Aexpr         { AMinus $1 $3 }
+

(A) Cannot be parsed as AExpr

+

(B) 6

+

(C) APlus (APlus (AConst 1) (AConst 2)) (AConst 3)

+

(D) APlus (AConst 1) (APlus (AConst 2) (AConst 3))

+


+









+

Running the Parser

+

First, we should tell the parser that the top-level non-terminal is AExpr:

+
%name aexpr
+

From the production rules and this line, happy generates a function aexpr that tries to parse a sequence of tokens as AExpr

+

We package this function together with the lexer and the evaluator into a handy function

+
evalString :: Env -> String -> Int
+


+

We can test the function like so:

+
λ> evalString [] "1 + 3 + 6"
+10
+
+λ> evalString [("x", 100), ("y", 20)] "x - y"
+???
+
+λ> evalString [] "2 * 5 + 5"
+???
+
+λ> evalString [] "2 - 1 - 1"
+???
+










+

Precedence and associativity

+
λ> evalString [] "2 * 5 + 5"
+20
+

The problem is that our grammar is ambiguous!

+

There are multiple ways of parsing the string 2 * 5 + 5, namely

+
    +
  • APlus (AMul (AConst 2) (AConst 5)) (AConst 5) (good)
  • +
  • AMul (AConst 2) (APlus (AConst 5) (AConst 5)) (bad!)
  • +
+

Wanted: tell happy that * has higher precedence than +!

+



+
λ> evalString [] "2 - 1 - 1"
+2
+

There are multiple ways of parsing 2 - 1 - 1, namely

+
    +
  • AMinus (AMinus (AConst 2) (AConst 1)) (AConst 1) (good)
  • +
  • AMinus (AConst 2) (AMinus (AConst 1) (AConst 1)) (bad!)
  • +
+

Wanted: tell happy that - is left-associative!

+



+

How do we communicate precedence and associativity to happy?

+





+

Solution 1: Grammar factoring

+

We can split the AExpr non-terminal into multiple “levels”

+
Aexpr : Aexpr '+' Aexpr2
+      | Aexpr '-' Aexpr2
+      | Aexpr2
+
+Aexpr2 : Aexpr2 '*' Aexpr3
+       | Aexpr2 '/' Aexpr3
+       | Aexpr3
+
+Aexpr3 : TNUM
+       | ID
+       | '(' Aexpr ')'
+

Intuition: AExpr2 “binds tighter” than AExpr, and AExpr3 is the tightest

+

Now I cannot parse the string 2 * 5 + 5 as

+
    +
  • AMul (AConst 2) (APlus (AConst 5) (AConst 5))

  • +
  • Why?

  • +
+





+

Solution 2: Parser directives

+

This problem is so common that parser generators have a special syntax for it!

+
%left '+' '-'
+%left '*' '/'
+

What this means:

+
    +
  • All our operators are left-associative
  • +
  • Operators on the lower line have higher precedence
  • +
+









+

That’s all folks!

+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/lectures/07-classes.html b/docs/lectures/07-classes.html new file mode 100644 index 0000000..c7df360 --- /dev/null +++ b/docs/lectures/07-classes.html @@ -0,0 +1,831 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

Typeclasses

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

Past two Weeks

+

How to implement language constructs?

+
    +
  • Local variables and scope
  • +
  • Environments and Closures
  • +
  • Parsing
  • +
+














+

Next two Weeks

+

Modern language features for structuring programs

+
    +
  • Overloading
  • +
  • Type classes
  • +
  • Monads
  • +
+














+

Overloading Operators: Arithmetic

+

The + operator works for a bunch of different types.

+

For Integer:

+
λ> 2 + 3
+5
+

for Double precision floats:

+
λ> 2.9 + 3.5
+6.4
+



















+

Overloading Comparisons

+

Similarly we can compare different types of values

+
λ> 2 == 3
+False
+
+λ>  [2.9, 3.5] == [2.9, 3.5]
+True
+
+λ> ("cat", 10) < ("cat", 2)
+False
+
+λ> ("cat", 10) < ("cat", 20)
+True
+



















+

Ad-Hoc Overloading

+

Seems unremarkable?

+

Languages since the dawn of time have supported “operator overloading”

+
    +
  • To support this kind of ad–hoc polymorphism

  • +
  • Ad-hoc: “created or done for a particular purpose as necessary.”

  • +
+

You really need to add and compare values of multiple types!

+
























+

Haskell has no caste system

+

No distinction between operators and functions

+
    +
  • All are first class citizens!
  • +
+

But then, what type do we give to functions like + and == ?

+
























+

QUIZ

+

Which of the following would be appropriate types for (+) ?

+

(A) (+) :: Integer -> Integer -> Integer

+

(B) (+) :: Double -> Double -> Double

+

(C) (+) :: a -> a -> a

+

(D) All of the above

+

(E) None of the above

+











+

Integer -> Integer -> Integer is bad because?

+
    +
  • Then we cannot add Doubles!
  • +
+













+

Double -> Double -> Double is bad because?

+
    +
  • Then we cannot add Doubles!
  • +
+















+

a -> a -> a is bad because?

+
    +
  • That doesn’t make sense, e.g. to add two Bool or two [Int] or two functions!
  • +
+

Type Classes for Ad Hoc Polymorphism

+

Haskell solves this problem with an insanely slick mechanism called type-classes, introduced by Wadler and Blott

+

+

BTW: The paper is one of the clearest examples of academic writing I have seen.

+

The next time you hear a curmudgeon say all the best CS was done in the 60s, just point them to the above.

+













+

Qualified Types

+

To see the right type, lets ask:

+
λ> :type (+)
+(+) :: (Num a) => a -> a -> a
+

We call the above a qualified type. Read it as +

+
    +
  • takes in two a values and returns an a value
  • +
+

for any type a that

+
    +
  • is a Num or
  • +
  • implements the Num interface or
  • +
  • is an instance of a Num.
  • +
+

The name Num can be thought of as a predicate or constraint over types

+ +













+

Some types are Nums

+

Examples include Integer, Double etc

+
    +
  • Any such values of those types can be passed to +.
  • +
+













+

Other types are not Nums

+

Examples include Char, String, functions etc,

+
    +
  • Values of those types cannot be passed to +.
  • +
+
λ> True + False
+
+<interactive>:15:6:
+    No instance for (Num Bool) arising from a use of+
+    In the expression: True + False
+    In an equation for ‘it’: it = True + False
+

Aha! Now those no instance for error messages should make sense!

+
    +
  • Haskell is complaining that True and False are of type Bool
  • +
  • and that Bool is not an instance of Num.
  • +
+













+

Type Class is a Set of Operations

+

A type class is a collection of operations (functions) that must exist for the underlying type.

+



















+

The Eq Type Class

+

The simplest type class is perhaps, Eq

+
class  Eq a  where
+  (==) :: a -> a -> Bool
+  (/=) :: a -> a -> Bool
+

A type a is an instance of Eq if there are two functions

+
    +
  • == and /=
  • +
+

That determine if two a values are respectively equal or disequal.

+













+

The Show Type Class

+

The type class Show requires that instances be convertible to String (which can then be printed out)

+
class  Show a  where
+  show :: a -> String
+

Indeed, we can test this on different (built-in) types

+
λ> show 2
+"2"
+
+λ> show 3.14
+"3.14"
+
+λ> show (1, "two", ([],[],[]))
+"(1,\"two\",([],[],[]))"
+

(Hey, whats up with the funny \"?)

+

When we type an expression into ghci, it computes the value and then calls show on the result. Thus, if we create a new type by

+
data Unshowable = A | B | C
+

and then create values of the type,

+
λ> let x = A
+λ> :type x
+x :: Unshowable
+

but then we cannot view them

+
λ> x
+
+<interactive>:1:0:
+    No instance for (Show Unshowable)
+      arising from a use of `print' at <interactive>:1:0
+    Possible fix: add an instance declaration for (Show Unshowable)
+    In a stmt of a 'do' expression: print it
+

and we cannot compare them!

+
λ> x == x
+
+<interactive>:1:0:
+    No instance for (Eq Unshowable)
+      arising from a use of `==' at <interactive>:1:0-5
+    Possible fix: add an instance declaration for (Eq Unshowable)
+    In the expression: x == x
+    In the definition of `it': it = x == x
+

Again, the previously incomprehensible type error message should make sense to you.

+













+

Creating Instances

+

Tell Haskell how to show or compare values of type Unshowable

+

By creating instances of Eq and Show for that type:

+
instance Eq Unshowable where
+  (==) A A = True           -- True if both inputs are A
+  (==) B B = True           -- ...or B
+  (==) C C = True           -- .. or C
+  (==) _ _ = False          -- otherwise
+
+  (/=) x y = not (x == y)   -- Test if `x == y` and negate result!
+

EXERCISE Lets create an instance for Show Unshowable

+
















+

Automatic Derivation

+

This is silly: we should be able to compare and view Unshowble “automatically”!

+

Haskell lets us automatically derive functions for some classes in the standard library.

+

To do so, we simply dress up the data type definition with

+
data Showable = A' | B' | C'
+  deriving (Eq, Show) -- tells Haskell to automatically generate instances
+

Now we have

+
λ> let x' = A'
+
+λ> :type x'
+x' :: Showable
+
+λ> x'
+A'
+
+λ> x' == x'
+True
+
+λ> x' == B'
+False
+
















+

Standard Typeclass Hierarchy

+

Let us now peruse the definition of the Num typeclass.

+
λ> :info Num
+class (Eq a, Show a) => Num a where
+  (+) :: a -> a -> a
+  (*) :: a -> a -> a
+  (-) :: a -> a -> a
+  negate :: a -> a
+  abs :: a -> a
+  signum :: a -> a
+  fromInteger :: Integer -> a
+

A type a is an instance of (i.e. implements) Num if

+
    +
  1. The type is also an instance of Eq and Show, and
  2. +
  3. There are functions for adding, multiplying, subtracting, negating etc values of that type.
  4. +
+

In other words in addition to the “arithmetic” operations, we can compare two Num values and we can view them (as a String.)

+

Haskell comes equipped with a rich set of built-in classes.

+
+
Standard Typeclass Hierarchy
+
+

In the above picture, there is an edge from Eq and Show to Num because for something to be a Num it must also be an Eq and Show.

+
















+

The Ord Typeclass

+

Another typeclass you’ve used already is the one for Ordering values:

+
λ> :info (<)
+class Eq a => Ord a where
+  ...
+  (<) :: a -> a -> Bool
+  ...
+

For example:

+
λ> 2 < 3
+True
+
+λ> "cat" < "dog"
+True
+
















+

QUIZ

+

Recall the datatype:

+
data Showable = A' | B' | C' deriving (Eq, Show)
+

What is the result of:

+
λ> A' < B'
+

(A) True (B) False (C) Type error (D) Run-time exception

+
















+

Using Typeclasses

+

Typeclasses integrate with the rest of Haskell’s type system.

+

Lets build a small library for Environments mapping keys k to values v

+
data Env k v
+  = Def  v              -- default value `v` to be used for "missing" keys
+  | Bind k v (Env k v)  -- bind key `k` to the value `v`
+  deriving (Show)
+
















+

An API for Env

+

Lets write a small API for Env

+
-- >>> let env0 = add "cat" 10.0 (add "dog" 20.0 (Def 0))
+
+-- >>> get "cat" env0
+-- 10
+
+-- >>> get "dog" env0
+-- 20
+
+-- >>> get "horse" env0
+-- 0
+

Ok, lets implement!

+
-- | 'add key val env' returns a new env that additionally maps `key` to `val`
+add :: k -> v -> Env k v -> Env k v
+add key val env = ???
+
+-- | 'get key env' returns the value of `key` and the "default" if no value is found
+
+get :: k -> Env k v -> v
+get key env = ???
+













+

Oops, y u no check?

+

Constraint Propagation

+

Lets delete the types of add and get and see what Haskell says their types are!

+
λ> :type get
+get :: (Eq k) => k -> v -> Env k v -> Env k v
+

Haskell tells us that we can use any k value as a key as long as the value is an instance of the Eq typeclass.

+

How, did GHC figure this out?

+
    +
  • If you look at the code for get you’ll see that we check if two keys are equal!
  • +
+














+

EXERCISE (Do at home)

+

Write an optimized version of

+
    +
  • add that ensures the keys are in increasing order,
  • +
  • get that gives up and returns the “default” the moment we see a key thats larger than the one we’re looking for.
  • +
+

(How) do you need to change the type of Env?

+

(How) do you need to change the types of get and add?

+







+

Explicit Signatures

+

While Haskell is pretty good about inferring types in general, there are cases when the use of type classes requires explicit annotations (which change the behavior of the code.)

+

For example, Read is a built-in typeclass, where any instance a of Read has a function

+
read :: (Read a) => String -> a
+

which can parse a string and turn it into an a.

+

That is, Read is the opposite of Show.

+

Quiz

+

What does the expression read "2" evaluate to?

+

(A) compile time error

+

(B) "2" :: String

+

(C) 2 :: Integer

+

(D) 2.0 :: Double

+

(E) run-time exception

+







+

Haskell is foxed!

+
    +
  • Doesn’t know what type to convert the string to!
  • +
  • Doesn’t know which of the read functions to run!
  • +
+

Did we want an Int or a Double or maybe something else altogether?

+

Thus, here an explicit type annotation is needed to tell Haskell what to convert the string to:

+
λ> (read "2") :: Int
+2
+
+λ> (read "2") :: Float
+2.0
+

Note the different results due to the different types.

+














+

Creating Typeclasses

+

Typeclasses are useful for many different things.

+

We will see some of those over the next few lectures.

+

Lets conclude today’s class with a quick example that provides a small taste.

+














+

JSON

+

JavaScript Object Notation or JSON is a simple format for transferring data around. Here is an example:

+
{ "name"    : "Ranjit"
+, "age"     : 41.0
+, "likes"   : ["guacamole", "coffee", "bacon"]
+, "hates"   : [ "waiting" , "grapefruit"]
+, "lunches" : [ {"day" : "monday",    "loc" : "zanzibar"}
+              , {"day" : "tuesday",   "loc" : "farmers market"}
+              , {"day" : "wednesday", "loc" : "harekrishna"}
+              , {"day" : "thursday",  "loc" : "faculty club"}
+              , {"day" : "friday",    "loc" : "coffee cart"} ]
+}
+

In brief, each JSON object is either

+
    +
  • a base value like a string, a number or a boolean,

  • +
  • an (ordered) array of objects, or

  • +
  • a set of string-object pairs.

  • +
+

A JSON Datatype

+

We can represent (a subset of) JSON values with the Haskell datatype

+
data JVal
+  = JStr  String
+  | JNum  Double
+  | JBool Bool
+  | JObj  [(String, JVal)]
+  | JArr  [JVal]
+  deriving (Eq, Ord, Show)
+

Thus, the above JSON value would be represented by the JVal

+
js1 =
+  JObj [("name", JStr "Ranjit")
+       ,("age",  JNum 41.0)
+       ,("likes",   JArr [ JStr "guacamole", JStr "coffee", JStr "bacon"])
+       ,("hates",   JArr [ JStr "waiting"  , JStr "grapefruit"])
+       ,("lunches", JArr [ JObj [("day",  JStr "monday")
+                                ,("loc",  JStr "zanzibar")]
+                         , JObj [("day",  JStr "tuesday")
+                                ,("loc",  JStr "farmers market")]
+                         , JObj [("day",  JStr "wednesday")
+                                ,("loc",  JStr "hare krishna")]
+                         , JObj [("day",  JStr "thursday")
+                                ,("loc",  JStr "faculty club")]
+                         , JObj [("day",  JStr "friday")
+                                ,("loc",  JStr "coffee cart")]
+                         ])
+       ]
+

Serializing Haskell Values to JSON

+

Lets write a small library to serialize Haskell values as JSON.

+

We could write a bunch of functions like

+
doubleToJSON :: Double -> JVal
+doubleToJSON = JNum
+
+stringToJSON :: String -> JVal
+stringToJSON = JStr
+
+boolToJSON   :: Bool -> JVal
+boolToJSON   = JBool
+

Serializing Collections

+

But what about collections, namely lists of things?

+
doublesToJSON    :: [Double] -> JVal
+doublesToJSON xs = JArr (map doubleToJSON xs)
+
+boolsToJSON      :: [Bool] -> JVal
+boolsToJSON xs   = JArr (map boolToJSON xs)
+
+stringsToJSON    :: [String] -> JVal
+stringsToJSON xs = JArr (map stringToJSON xs)
+

This is getting rather tedious

+
    +
  • We are rewriting the same code :(
  • +
+







+

Serializing Collections (refactored with HOFs)

+

You could abstract by making the individual-element-converter a parameter

+
xsToJSON :: (a -> JVal) -> [a] -> JVal
+xsToJSON f xs = JArr (map f xs)
+
+xysToJSON :: (a -> JVal) -> [(String, a)] -> JVal
+xysToJSON f kvs = JObj [ (k, f v) | (k, v) <- kvs ]
+

But this is *still rather tedious** as you have to pass in the individual data converter (yuck)

+
λ> doubleToJSON 4
+JNum 4.0
+
+λ> xsToJSON stringToJSON ["coffee", "guacamole", "bacon"]
+JArr [JStr "coffee",JStr "guacamole",JStr "bacon"]
+
+λ> xysToJSON stringToJSON [("day", "monday"), ("loc", "zanzibar")]
+JObj [("day",JStr "monday"),("loc",JStr "zanzibar")]
+

This gets more hideous when you have richer objects like

+
lunches = [ [("day", "monday"),    ("loc", "zanzibar")]
+          , [("day", "tuesday"),   ("loc", "farmers market")]
+          ]
+

because we have to go through gymnastics like

+
λ> xsToJSON (xysToJSON stringToJSON) lunches
+JArr [ JObj [("day",JStr "monday")   ,("loc",JStr "zanzibar")]
+     , JObj [("day",JStr "tuesday")  ,("loc",JStr "farmers market")]
+     ]
+

Yikes. So much for readability

+

Is it too much to ask for a magical toJSON that just works?

+

Typeclasses To The Rescue

+

Lets define a typeclass that describes types a that can be converted to JSON.

+
class JSON a where
+  toJSON :: a -> JVal
+

Now, just make all the above instances of JSON like so

+
instance JSON Double where
+  toJSON = JNum
+
+instance JSON Bool where
+  toJSON = JBool
+
+instance JSON String where
+  toJSON = JStr
+

This lets us uniformly write

+
λ> toJSON 4
+JNum 4.0
+
+λ> toJSON True
+JBool True
+
+λ> toJSON "guacamole"
+JStr "guacamole"
+

Bootstrapping Instances

+

The real fun begins when we get Haskell to automatically bootstrap the above functions to work for lists and key-value lists!

+
instance JSON a => JSON [a] where
+  toJSON xs = JArr (map toJSON xs)
+

The above says, if a is an instance of JSON, that is, if you can convert a to JVal then here’s a generic recipe to convert lists of a values!

+
λ> toJSON [True, False, True]
+JArr [JBln True, JBln False, JBln True]
+
+λ> toJSON ["cat", "dog", "Mouse"]
+JArr [JStr "cat", JStr "dog", JStr "Mouse"]
+

or even lists-of-lists!

+
λ> toJSON [["cat", "dog"], ["mouse", "rabbit"]]
+JArr [JArr [JStr "cat",JStr "dog"],JArr [JStr "mouse",JStr "rabbit"]]
+

We can pull the same trick with key-value lists

+
instance (JSON a) => JSON [(String, a)] where
+  toJSON kvs = JObj [ (k, toJSON v) | (k, v) <- kvs ]
+

after which, we are all set!

+
λ> toJSON lunches
+JArr [ JObj [ ("day",JStr "monday"), ("loc",JStr "zanzibar")]
+     , JObj [("day",JStr "tuesday"), ("loc",JStr "farmers market")]
+     ]
+

It is also useful to bootstrap the serialization for tuples (up to some fixed size) so we can easily write “non-uniform” JSON objects where keys are bound to values with different shapes.

+
instance (JSON a, JSON b) => JSON ((String, a), (String, b)) where
+  toJSON ((k1, v1), (k2, v2)) =
+    JObj [(k1, toJSON v1), (k2, toJSON v2)]
+
+instance (JSON a, JSON b, JSON c) => JSON ((String, a), (String, b), (String, c)) where
+  toJSON ((k1, v1), (k2, v2), (k3, v3)) =
+    JObj [(k1, toJSON v1), (k2, toJSON v2), (k3, toJSON v3)]
+
+instance (JSON a, JSON b, JSON c, JSON d) => JSON ((String, a), (String, b), (String, c), (String,d)) where
+  toJSON ((k1, v1), (k2, v2), (k3, v3), (k4, v4)) =
+    JObj [(k1, toJSON v1), (k2, toJSON v2), (k3, toJSON v3), (k4, toJSON v4)]
+
+instance (JSON a, JSON b, JSON c, JSON d, JSON e) => JSON ((String, a), (String, b), (String, c), (String,d), (String, e)) where
+  toJSON ((k1, v1), (k2, v2), (k3, v3), (k4, v4), (k5, v5)) =
+    JObj [(k1, toJSON v1), (k2, toJSON v2), (k3, toJSON v3), (k4, toJSON v4), (k5, toJSON v5)]
+

Now, we can simply write

+
hs = (("name"   , "Ranjit")
+     ,("age"    , 41.0)
+     ,("likes"  , ["guacamole", "coffee", "bacon"])
+     ,("hates"  , ["waiting", "grapefruit"])
+     ,("lunches", lunches)
+     )
+

which is a Haskell value that describes our running JSON example, and can convert it directly like so

+
js2 = toJSON hs
+

EXERCISE: Serializing Environments

+

To wrap everything up, lets write a routine to serialize our Env

+
instance JSON (Env k v) where
+  toJSON env = ???
+

and presto! our serializer just works

+
λ> env0
+Bind "cat" 10.0 (Bind "dog" 20.0 (Def 0))
+
+λ> toJSON env0
+JObj [ ("cat", JNum 10.0)
+     , ("dog", JNum 20.0)
+     , ("def", JNum 0.0)
+     ]
+

Thats it for today.

+

We will see much more type class awesomeness in the next few lectures…

+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/lectures/07-types.html b/docs/lectures/07-types.html new file mode 100644 index 0000000..1acc23b --- /dev/null +++ b/docs/lectures/07-types.html @@ -0,0 +1,936 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

Polymorphism and Type Inference

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

Plan for this week

+

Past two weeks:

+

How do we implement a tiny functional language?

+
    +
  1. Interpreter: how do we evaluate a program given its AST?

  2. +
  3. Operational semantics: formalizing the interpreter

  4. +
  5. Parser: how do we convert strings to ASTs?

  6. +
+

This week: adding types

+

How do we check statically if our programs “make sense”?

+
    +
  1. Type system: formalizing the intuition about which expressions have which types

  2. +
  3. Type inference: computing the type of an expression

  4. +
+










+

Reminder: Nano2

+
e ::= n | x            -- numbers, vars
+    | e1 + e2          -- arithmetic
+    | \x -> e          -- abstraction
+    | e1 e2            -- application
+    | let x = e1 in e2 -- let binding
+










+

QUIZ

+

Which one of these Nano2 programs is well-typed?

+

(A) (\x -> x) + 1

+

(B) 1 2

+

(C) let f = \x -> x + 1 in f (\y -> y)

+

(D) \x -> \y -> x y

+

(E) \x -> x x

+


+









+

Type system for Nano2

+

A type system defines what types an expression can have

+

To define a type system we need to define:

+
    +
  • the syntax of types: what do types look like?
  • +
  • the static semantics of our language (i.e. the typing rules): assign types to expressions
  • +
+










+

Type system: take 1

+

Syntax of types:

+
T ::= Int       -- integers
+    | T1 -> T2  -- function types
+


+

Now we want to define a typing relation e :: T (e has type T)

+


+

We define this relation inductively through a set of typing rules:

+
[T-Num]  n :: Int
+
+         e1 :: Int    e2 :: Int   -- premises
+[T-Add]  ----------------------
+            e1 + e2 :: Int        -- conclusion
+            
+[T-Var]   x :: ???
+


+

What is the type of a variable?

+



+

We have to remember what type of expression it was bound to!

+









+

Type Environment

+

An expression has a type in a given type environment (also called context), which maps all its free variables to their types

+

G = x1:T1, x2:T2, ..., xn:Tn

+


+

Our typing relation should include the context G:

+

G |- e :: T (e has type T in context G)

+









+

Typing rules: take 2

+
[T-Num]  G |- n :: Int
+
+         G |- e1 :: Int   G |- e2 :: Int
+[T-Add]  -------------------------------
+               G |- e1 + e2 :: Int
+
+[T-Var]  G |- x :: T          if x:T in G
+
+          G, x:T1 |- e :: T2
+[T-Abs] ------------------------
+        G |- \x -> e :: T1 -> T2
+        
+        G |- e1 :: T1 -> T2   G |- e2 :: T1
+[T-App] -----------------------------------
+                 G |- e1 e2 :: T2
+        
+        G |- e1 :: T1   G, x:T1 |- e2 :: T2
+[T-Let] ------------------------------------
+           G |- let x = e1 in e2 :: T2
+


+

An expression e has type T in G if we can derive G |- e :: T using these rules

+

An expression e is well-typed in G if we can derive G |- e :: T for some type T

+
    +
  • and ill-typed otherwise
  • +
+






+

Examples

+

Example 1:

+

Let’s derive: [] |- (\x -> x) 2 :: Int

+
[T-Var] -------------------
+        [x:Int] |- x :: Int
+[T-Abs] -------------------            -------------- [T-Num]
+        [] |- \x -> x :: Int -> Int    [] |- 2 :: Int
+[T-App] -----------------------------------------------
+        [] |- (\x -> x) 2 :: Int
+

But we cannot derive: [] |- 1 2 :: T for any type T

+
    +
  • Why?
  • +
  • T-App only applies when LHS has a function type, but there’s no rule to derive a function type for 1
  • +
+



+

Example 2:

+

Let’s derive: [] |- let x = 1 in x + 2 :: Int

+
      ?
+[?]   -----------------------------------------------
+        [] |- let x = 1 in x + 2 :: Int
+

But we cannot derive: [] |- let x = \y -> y in x + 2 :: T for any type T

+



+

Example 3:

+

We cannot derive: [] |- (\x -> x x) :: T for any type T

+









+

A note about typing rules

+

According to these rules, an expression can have zero, one, or many types

+
    +
  • examples?
  • +
+









+

\x -> x has many types:

+
    +
  • we can derive [] |- \x -> x :: Int -> Int
  • +
  • or [] |- \x -> x :: (Int -> Int) -> (Int -> Int)
  • +
  • or T -> T for any concrete T
  • +
+


+

We would like every well-typed expression to have a single most general type!

+
    +
  • most general type = allows most uses
  • +
  • infer type once and reuse later
  • +
+









+

QUIZ

+

Is this program well-typed according to your intuition and according to our rules?

+
let id = \x -> x in
+  let y = id 5 in
+    id (\z -> z + y)
+

(A) Me: okay, rules: okay

+

(B) Me: okay, rules: nope

+

(C) Me: nope, rules: okay

+

(D) Me: nope, rules: nope

+


+

Double identity

+
let id = \x -> x in
+  let y = id 5 in
+    id (\z -> z + y)
+

Intuitively this program looks okay, but our type system rejects it:

+
    +
  • in the first application, id needs to have type Int -> Int
  • +
  • in the second application, id needs to have type (Int -> Int) -> (Int -> Int)
  • +
  • the type system forces us to pick just one type for each variable, such as id :(
  • +
+



+

What can we do?

+









+

Polymorphic types

+

Intuitively, we can describe the type of id like this:

+
    +
  • it’s a function type where
  • +
  • the argument type can be any type T
  • +
  • the return type is then also T
  • +
+


+

We formalize this intuition as a polymorphic type: forall a . a -> a

+
    +
  • where a is a (bound) type variable

  • +
  • also called a type scheme

  • +
  • Haskell also has polymorphic types, but you don’t usually write forall a.

  • +
+


+

We can instantiate this scheme into different types by replacing a in the body with some type, e.g.

+
    +
  • instantiating with Int yields Int -> Int
  • +
  • instantiating with Int -> Int yields (Int -> Int) -> Int -> Int
  • +
  • etc.
  • +
+









+

Inference with polymorphic types

+

With polymorphic types, we can derive e :: Int -> Int where e is

+
let id = \x -> x in
+  let y = id 5 in
+    id (\z -> z + y)
+

At a high level, inference works as follows:

+
    +
  1. When we have to pick a type T for x, we pick a fresh type variable a

  2. +
  3. So the type of \x -> x comes out as a -> a

  4. +
  5. We can generalize this type to forall a . a -> a

  6. +
  7. When we apply id the first time, we instantiate this polymorphic type with Int

  8. +
  9. When we apply id the second time, we instantiate this polymorphic type with Int -> Int

  10. +
+

Let’s formalize this intuition as a type system!

+









+

Type system: take 3

+

Syntax of types

+
-- Mono-types
+T ::= Int       -- integers
+    | T1 -> T2  -- function types
+    | a         -- NEW: type variable
+    
+-- NEW: Poly-types (type schemes)
+S ::= T            -- mono-type
+    | forall a . S -- polymorphic type
+

where a ∈ TVar, T ∈ Type, S ∈ Poly

+



+

Type Environment

+

The type environment now maps variables to poly-types: G : Var -> Poly

+
    +
  • example, G = [z: Int, id: forall a . a -> a]
  • +
+



+

Type Substitutions

+

We need a mechanism for replacing all type variables in a type with another type

+

A type substitution is a finite map from type variables to types: U : TVar -> Type

+
    +
  • example: U1 = [a / Int, b / (c -> c)]
  • +
+


+

To apply a substitution U to a type T means replace all type vars in T with whatever they are mapped to in U

+
    +
  • example 1: U1 (a -> a) = Int -> Int
  • +
  • example 2: U1 Int = Int
  • +
+









+

QUIZ

+

What is the result of the following substitution application?

+
[a / Int, b / c -> c] (b -> d -> b)
+

(A) c -> d -> c

+

(B) (c -> c) -> d -> (c -> c)

+

(C) Error: no mapping for type variable d

+

(D) Error: type variable a is unused

+


+








+

Typing rules

+

We need to change the typing rules so that:

+
    +
  1. Variables (and their definitions) can have polymorphic types
  2. +
+
[T-Var]  G |- x :: S          if x:S in G
+        
+        G |- e1 :: S   G, x:S |- e2 :: T
+[T-Let] ------------------------------------
+           G |- let x = e1 in e2 :: T
+
    +
  1. We can instantiate a type scheme into a type
  2. +
+
         G |- e :: forall a . S    
+[T-Inst] ----------------------
+          G |- e :: [a / T] S
+
    +
  1. We can generalize a type with free type variables into a type scheme
  2. +
+
             G |- e :: S    
+[T-Gen] ---------------------- if not (a in FTV(G))
+        G |- e :: forall a . S
+

The rest of the rules are the same:

+
[T-Num]  G |- n :: Int
+
+         G |- e1 :: Int   G |- e2 :: Int
+[T-Add]  -------------------------------
+               G |- e1 + e2 :: Int
+
+          G, x:T1 |- e :: T2
+[T-Abs] ------------------------
+        G |- \x -> e :: T1 -> T2
+        
+        G |- e1 :: T1 -> T2   G |- e2 :: T1
+[T-App] -----------------------------------
+                 G |- e1 e2 :: T2        
+







+

Examples

+

Example 1

+

Let’s derive: [] |- \x -> x :: forall a . a -> a

+
[T-Var] ---------------
+        [x:a] |- x :: a
+[T-Abs] -----------------------
+        [] |- \x -> x :: a -> a
+[T-Gen] ----------------------------------- not (a in FTV([]))
+        [] |- \x -> x :: forall a . a -> a
+


+

Can we derive: [x:a] |- x :: forall a . a?

+



+

Example 2

+

Let’s derive: G1 |- id 5 :: Int where G1 = [id : (forall a . a -> a)]:

+
    ?
+[?] ----------------
+    G1 |- id 5 :: Int
+



+

Example 3

+

Finally, we can derive:

+
(let id = \x -> x in
+  let y = id 5 in
+    id (\z -> z + y)) :: Int -> Int
+
                                                easy
+    [T-Var]--------------------------     ------------------ [Add]
+          G2 |- id::forall a.a -> a       G3 |- z + y :: Int
+[T-Inst]------------------------------  -------------------------[T-Abs]
+        G2 |- id::(Int->Int)->Int->Int  G2 |- \z -> z+y :: Int->Int
+                                |                          |
+               example 2        |                          |
+           -----------------   ----------------------------------[T-App]
+           G1 |- id 5 :: Int   G2 |- id (\z -> z+y) :: Int -> Int
+   [T-Let] ------------------------------------------------------
+                              G1 |- let y = id 5 in ... :: Int -> Int
+                                                         |
+                  example 1                              |
+[T-Abs] --------------------------------                 |
+        [] |- \x -> x :: forall a.a -> a                 |
+[T-Let] -------------------------------------------------------------
+        [] |- let id = \x -> x in ... :: Int -> Int
+

where

+
    +
  • G1 = [id : (forall a . a -> a)]
  • +
  • G2 = [y : Int, id : (forall a . a -> a)]
  • +
  • G3 = [z : Int, y : Int, id : (forall a . a -> a)]
  • +
+









+

Type inference algorithm

+

Our ultimate goal is to implement a Haskell function infer which

+
    +
  • given a context G and an expression e
  • +
  • returns a type T such that G |- e :: T
  • +
  • or reports a type error if e is ill-typed in G
  • +
+





+

Representing types

+

First, let’s define a Haskell datatype to represent Nano2 types:

+
data Type
+  = TInt             -- Int
+  | Type :=> Type    -- T1 -> T2
+  | TVar TVar        -- a, b, c
+    
+data Poly = Mono Type 
+          | Forall TVar Poly
+          
+type TVar = String
+type TEnv = [(Id, Poly)]    -- type environment
+type Subst = [(TVar, Type)] -- type substitution
+





+

Inference: main idea

+

Let’s implement infer like this:

+
    +
  1. Depending on what kind of expression e is, find a typing rule that applies to it

  2. +
  3. If the rule has premises, recursively call infer to obtain the types of sub-expressions

  4. +
  5. Combine the types of sub-expression according to the conclusion of the rule

  6. +
  7. If no rule applies, report a type error

  8. +
+


+
-- | This is not the final version!!!
+infer :: TypeEnv -> Expr -> Type
+infer _    (ENum _)     = TInt
+infer tEnv (EVar var)   = lookup var tEnv
+infer tEnv (EAdd e1 e2) = 
+  if t1 == TInt && t2 == TInt 
+    then return TInt 
+    else throw "type error: + expects Int operands"
+  where
+    t1 = infer tEnv e1
+    t2 = infer tEnv e2
+...    
+


+

This doesn’t quite work (for other cases). Why?

+





+

Inference: tricky bits

+

The trouble is that our typing rules are nondeterministic!

+
    +
  • When building derivations, sometimes we had to guess how to proceed
  • +
+

Problem 1: Guessing a type

+
       -- oh, now we know!
+[T-Var]----------------
+       [x:?] |- x: Int    [x:?] |- 1 :: Int
+[T-Add]----------------------------
+       [x:?] |- x + 1 :: ?? -- what should "?" be?
+[T-Abs]------------------------------
+       [] |- (\x -> x + 1) :: ? -> ??
+

So, if we want to implement

+
infer tEnv (ELam x e) = tX :=> tBody
+  where
+    tEnv'  = extendTEnv x tX tEnv
+    tX     = ??? -- what do we put here?
+    tBody  = infer tEnv' e
+...    
+


+

Problem 2: Guessing when to generalize

+

In the derivation for

+
(let id = \x -> x in
+  let y = id 5 in
+    id (\z -> z + y)) :: Int -> Int
+

we had to guess that the type of id should be generalized into forall a . a -> a

+


+

Let’s deal with problem 1 first

+









+

Constraint-based type inference

+
       -- oh, now we know!
+[T-Var]----------------
+       [x:?] |- x: Int    [x:?] |- 1 :: Int
+[T-Add]----------------------------
+       [x:?] |- x + 1 :: ?? -- what should "?" be?
+[T-Abs]------------------------------
+       [] |- (\x -> x + 1) :: ? -> ??
+

Main idea:

+
    +
  1. Whenever you need to “guess” a type, don’t.

    +
      +
    • just return a fresh type variable

    • +
    • fresh = not used anywhere else in the program

    • +
  2. +
  3. Whenever a rule imposes a constraint on a type (i.e. says it should have certain form):

    +
      +
    • try to find the right substitution for the free type vars to satisfy the constraint

    • +
    • this step is called unification

    • +
  4. +
+






+

Example

+

Let’s infer the type of \x -> x + 1:

+
-- TEnv     Expression    Step           Subst     Inferred type
+
+1  []       \x -> x + 1   [T-Abs]        []
+2  [x:a0]         x + 1   [T-Add]        
+3                 x       [T-Var]                  a0
+4                 x + 1   unify a0 Int   [a0/Int]
+5  [x:Int]            1   [T-Num]                  Int
+6                 x + 1   unify Int Int   
+7                 x + 1                            Int 
+8  []       \x -> x + 1                            Int -> Int          
+


+
    +
  1. Infer the type of (\x -> x + 1) in [] (apply [T-Abs])

  2. +
  3. For the type of x, pick fresh type variable (say, a0); infer the type of x + 1 in [x:a0] (apply [T-Add])

  4. +
  5. Infer the type of x in [x:a0] (apply [T-Var]); result: a0

  6. +
  7. [T-Add] imposes a constraint: its LHS must be of type Int, so unify a0 and Int and update the current substitution to [a0 / Int]

  8. +
  9. Apply the current substitution [a0/Int] to the type environment [x:a0] to get [x:Int]. Infer the type of 1 in [x:Int] (apply [T-Num]); result: Int

  10. +
  11. [T-Add] imposes a constraint: its RHS must be of type Int, so unify Int and Int; current substitution doesn’t change

  12. +
  13. By conclusion of [T-Add]: return Int as the inferred type

  14. +
  15. By conclusion of [T-Lam]: return Int -> Int as the inferred type

  16. +
+









+

Unification

+

The unification problem: given two types T1 and T2, find a type substitution U such that U T1 = U T2.

+

Such a substitution is called a unifier of T1 and T2

+

Examples:

+

The unifier of:

+
a         and  Int         is [a / Int]
+
+a -> a    and  Int -> Int  is [a / Int]
+
+a -> Int  and  Int -> b    is ???
+
+Int       and  Int         is ???
+
+a         and  a           is ???
+
+Int       and Int -> Int   is ???
+
+Int       and a -> a       is ???
+
+a         and a -> a       is ???
+









+

QUIZ

+

What is the unifier of the following two types?

+
1. a -> Int -> Int
+2. b -> c
+

(A) Cannot unify

+

(B) [a / Int, b / Int -> Int, c / Int]

+

(C) [a / Int, b / Int, c / Int -> Int]

+

(D) [b / a, c / Int -> Int]

+

(E) [a / b, c / Int -> Int]

+










+

(C), (D) and (E) are all unifiers!

+

But somehow (D) and (E) are better than (C)

+
    +
  • they make the least commitment required to make these types equal

  • +
  • this is called the most general unifier

  • +
+










+

Infer: take 2

+

Let’s add constraint-based typing to infer!

+
-- | Now has to keep track of current substitution!
+infer :: Subst -> TypeEnv -> Expr -> (Subst, Type)
+infer sub _    (ENum _)     = (sub, TInt)
+infer sub tEnv (EVar var)   = (sub, lookup var tEnv)
+
+-- Lambda case: simply generate fresh type variable! 
+infer sub tEnv (ELam x e) = (sub1, tX' :=> tBody)
+  where
+    tEnv'          = extendTEnv x tX tEnv
+    tX             = freshTV -- we'll get to this
+    (sub1, tBody)  = infer sub tEnv' e
+    tX'            = apply sub1 tX
+
+-- Add case: recursively infer types of operands 
+-- and enforce constraint that they are both Int
+infer sub tEnv (EAdd e1 e2) = (sub4, TInt)
+  where
+    (sub1, t1) = infer sub tEnv e1   -- 1. infer type of e1
+    sub2       = unify sub1 t1 Int   -- 2. enforce constraint: t1 is Int
+    tEnv'      = apply sub2 tEnv     -- 3. apply substitution to context      
+    (sub3, t2) = infer sub2 tEnv' e2 -- 4. infer type of e2 in new ctx
+    sub4       = unify sub3 t2 Int   -- 5. enforce constraint: t2 is Int
+


+

Why are all these steps necessary? Can’t we just return (sub, TInt)?

+









+

QUIZ

+

Which of these programs will type-check if we skip step 3?

+
infer sub tEnv (EAdd e1 e2) = (sub4, TInt)
+  where
+    (sub1, t1) = infer sub tEnv e1   -- 1. infer type of e1
+    sub2       = unify sub1 t1 Int   -- 2. enforce constraint: t1 is Int
+    tEnv'      = apply sub2 tEnv     -- 3. apply substitution to context      
+    (sub3, t2) = infer sub2 tEnv' e2 -- 4. infer type of e2 in new ctx
+    sub4       = unify sub3 t2 Int   -- 5. enforce constraint: t2 is Int
+

(A) 1 2 + 3

+

(B) 1 + 2 3

+

(C) (\x -> x) + 1

+

(D) 1 + (\x -> x)

+

(E) \x -> x + x 5

+


+









+

Fresh type variables

+
-- | Now has to keep track of current substitution!
+infer :: Subst -> TypeEnv -> Expr -> (Subst, Type)
+
+-- Lambda case: simply generate fresh type variable! 
+infer tEnv (ELam x e) = tX :=> tBody
+  where
+    tEnv'  = extendTEnv x tX tEnv
+    tX     = freshTV -- how do we do this?
+    tBody  = infer tEnv' e    
+



+

Intended behavior:

+
    +
  • First time we call freshTV it returns a0
  • +
  • Second time it returns a1
  • +
  • Third time it returns a2
  • +
  • .. and so on
  • +
+



+

Can we do that in Haskell?

+










+

Polymorphism: the final frontier

+

Back to double identity:

+
let id = \x -> x in   -- Must generalize the type of id
+  let y = id 5 in     -- Instantiate with Int
+    id (\z -> z + y)  -- Instantiate with (Int -> Int)
+


+
    +
  • When should we to generalize a type like a -> a into a polymorphic type like forall a . a -> a?

  • +
  • When should we instantiate a polymorphic type like forall a . a -> a and with what?

  • +
+


+

Generalization and instantiation:

+
    +
  • Whenever we infer a type for a let-defined variable, generalize it!

    +
      +
    • it’s safe to do so, even when not strictly necessary
    • +
  • +
  • Whenever we see a variable with a polymorphic type, instantiate it

    +
      +
    • with what type?

    • +
    • well, what do we use when we don’t know what type to use?

    • +
    • fresh type variables!

    • +
  • +
+










+

Example

+

Let’s infer the type of let id = \x -> x in id 5:

+
-- TEnv   Expression             Step           Subst           Type
+
+1  []     let id=\x->x in id 5   [T-Let]        []
+2                \x->x           [T-Abs]         
+3  [x:a0]            x           [T-Var]                        a0
+4                \x->x                                          a0 -> a0
+5  []     let id=\x->x in id 5   generalize a0
+6  tEnv                   id 5   [T-App]          
+7                         id     [T-Var]                     
+8                         id     instantiate                    a1 -> a1
+9                            5   [T-Num]                        Int
+10                        id 5   unify (a1->a1) 
+                                      (Int->a2) [a1/Int,a2/Int]
+10                        id 5                                  Int
+11 []     let id=\x->x in id 5                                  Int 
+

Here tEnv = [id : forall a0.a0->a0]

+










+

Infer: take 3

+

Let’s add polymorphism to infer!

+
infer :: Subst -> TypeEnv -> Expr -> (Subst, Type)
+
+-- Variable case: add instantiation!
+infer sub tEnv (EVar var)      = (sub, t)
+  where
+    t = instantiate (lookup var tEnv)
+
+-- Let case: add generalization!
+infer sub tEnv (ELet x e1 e2)  = infer sub1 tEnv2 e2
+  where
+    (sub1, t1) = infer sub tEnv e1     -- 1. infer type of e1
+    tEnv1      = apply sub1 tEnv       -- 2. apply substitution to context
+    s1         = generalize tEnv1 t1   -- 3. generalize
+    tEnv2      = extendTEnv x s1 tEnv1 -- 4. add x to context
+

Note: this version still doesn’t properly handle

+
    +
  • generating fresh type variables
  • +
  • recursive definitions
  • +
+

You’ll have to do that in the homework!

+










+

What we learned this week

+

Type system: a set of rules about which expressions have which types

+

Type environment (or context): a mapping of variables to their types

+

Polymorphic type: a type parameterized with type variables that can be instantiated with any concrete type

+

Type substitution: a mapping of type variables to types; you can apply a substitution to a type by replacing all its variables with their values in the substitution

+

Unifier of two types: a substitution that makes them equal; unification is the process of finding a unifier

+

Type inference: an algorithm to determine the type of an expression

+

Constraint-based type inference: a type inference technique that uses fresh type variables and unification

+

Generalization: turning a mono-type with free type variables into a polymorphic type (by binding its variables with a forall)

+

Instantiation: turning a polymorphic type into a mono-type by substituting type variables in its body with some types

+










+

That’s all folks

+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/lectures/08-monads.html b/docs/lectures/08-monads.html new file mode 100644 index 0000000..92939de --- /dev/null +++ b/docs/lectures/08-monads.html @@ -0,0 +1,617 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

Monads

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

Abstracting Code Patterns

+

a.k.a. Don’t Repeat Yourself

+

Lists

+
data List a
+  = []
+  | (:) a (List a)
+




+

Rendering the Values of a List

+
-- >>> incList [1, 2, 3]
+-- ["1", "2", "3"]
+
+showList        :: [Int] -> [String]
+showList []     =  []
+showList (n:ns) =  show n : showList ns
+




+

Squaring the values of a list

+
-- >>> sqrList [1, 2, 3]
+-- 1, 4, 9
+
+sqrList        :: [Int] -> [Int]
+sqrList []     =  []
+sqrList (n:ns) =  n^2 : sqrList ns
+




+

Common Pattern: map over a list

+

Refactor iteration into mapList

+
mapList :: (a -> b) -> [a] -> [b]
+mapList f []     = []
+mapList f (x:xs) = f x : mapList f xs
+

Reuse map to implement inc and sqr

+
showList xs = map (\n -> show n) xs
+
+sqrList  xs = map (\n -> n ^ 2)  xs
+

Trees

+
data Tree a
+  = Leaf
+  | Node a (Tree a) (Tree a)
+




+

Incrementing and Squaring the Values of a Tree

+
-- >>> showTree (Node 2 (Node 1 Leaf Leaf) (Node 3 Leaf Leaf))
+-- (Node "2" (Node "1" Leaf Leaf) (Node "3" Leaf Leaf))
+
+showTree :: Tree Int -> Tree String
+showTree Leaf         = ???
+showTree (Node v l r) = ???
+
+-- >>> sqrTree (Node 2 (Node 1 Leaf Leaf) (Node 3 Leaf Leaf))
+-- (Node 4 (Node 1 Leaf Leaf) (Node 9 Leaf Leaf))
+
+sqrTree :: Tree Int -> Tree Int
+sqrTree Leaf         = ???
+sqrTree (Node v l r) = ???
+

QUIZ: map over a Tree

+

Refactor iteration into mapTree! What should the type of mapTree be?

+
mapTree :: ???
+
+showTree t = mapTree (\n -> show n) t
+sqrTree  t = mapTree (\n -> n ^ 2)  t
+
+{- A -} (Int -> Int)    -> Tree Int -> Tree Int
+{- B -} (Int -> String) -> Tree Int -> Tree String
+{- C -} (Int -> a)      -> Tree Int -> Tree a
+{- D -} (a -> a)        -> Tree a   -> Tree a
+{- E -} (a -> b)        -> Tree a   -> Tree b
+









+

Lets write mapTree

+
mapTree :: (a -> b) -> Tree a -> Tree b
+mapTree f Leaf         = ???
+mapTree f (Node v l r) = ???
+

QUIZ

+

Wait … there is a common pattern across two datatypes

+
mapList :: (a -> b) -> List a -> List b    -- List
+mapTree :: (a -> b) -> Tree a -> Tree b    -- Tree
+

Lets make a class for it!

+
class Mappable t where
+  map :: ???
+

What type should we give to map?

+
{- A -} (b -> a) -> t b    -> t a
+{- B -} (a -> a) -> t a    -> t a
+{- C -} (a -> b) -> [a]    -> [b]
+{- D -} (a -> b) -> t a    -> t b
+{- E -} (a -> b) -> Tree a -> Tree b
+







+

Reuse Iteration Across Types

+

Haskell’s libraries use the name Functor instead of Mappable

+
instance Functor [] where
+  fmap = mapList
+
+instance Functor Tree where
+  fmap = mapTree
+

And now we can do

+
-- >>> fmap (\n -> n + 1) (Node 2 (Node 1 Leaf Leaf) (Node 3 Leaf Leaf))
+-- (Node 4 (Node 1 Leaf Leaf) (Node 9 Leaf Leaf))
+
+-- >>> fmap show [1,2,3]
+-- ["1", "2", "3"]
+

Exercise: Write a Functor instance for Result

+
data Result  a
+  = Error String
+  | Ok    a
+
+instance Functor Result where
+  fmap f (Error msg) = ???
+  fmap f (Ok val)    = ???
+

When you’re done you should see

+
-- >>> fmap (\n -> n ^ 2) (Node 2 (Node 1 Leaf Leaf) (Node 3 Leaf Leaf))
+-- (Node 4 (Node 1 Leaf Leaf) (Node 9 Leaf Leaf))
+
+-- >>> fmap (\n -> n ^ 2) (Error "oh no") 
+-- Error "oh no"
+
+-- >>> fmap (\n -> n ^ 2) (Ok 9) 
+-- Ok 81
+

Next: A Class for Sequencing

+

Recall our old Expr datatype

+
data Expr
+  = Number Int
+  | Plus   Expr Expr
+  | Div    Expr Expr
+  deriving (Show)
+
+eval :: Expr -> Int
+eval (Number n)   = n
+eval (Plus e1 e2) = eval e1   +   eval e2
+eval (Div  e1 e2) = eval e1 `div` eval e2
+
+-- >>> eval (Div (Number 6) (Number 2))
+-- 3
+

But, what is the result

+
-- >>> eval (Div (Number 6) (Number 0))
+-- *** Exception: divide by zero
+

A crash! Lets look at an alternative approach to avoid dividing by zero.

+

The idea is to return a Result Int (instead of a plain Int)

+
    +
  • If a sub-expression had a divide by zero, return Error "..."
  • +
  • If all sub-expressions were safe, then return the actual Result v
  • +
+
eval :: Expr -> Result Int
+eval (Number n)   = Value n
+eval (Plus e1 e2) = case e1 of
+                      Error err1 -> Error err1
+                      Value v1   -> case e2 of
+                                      Error err2 -> Error err2
+                                      Value v1   -> Result (v1 + v2)
+eval (Div e1 e2)  = case e1 of
+                      Error err1 -> Error err1
+                      Value v1   -> case e2 of
+                                      Error err2 -> Error err2
+                                      Value v1   -> if v2 == 0 
+                                                      then Error ("yikes dbz:" ++ show e2)
+                                                      else Value (v1 `div` v2)
+

The good news, no nasty exceptions, just a plain Error result

+
λ> eval (Div (Number 6) (Number 2))
+Value 3
+λ> eval (Div (Number 6) (Number 0))
+Error "yikes dbz:Number 0"
+λ> eval (Div (Number 6) (Plus (Number 2) (Number (-2))))
+Error "yikes dbz:Plus (Number 2) (Number (-2))"
+

The bad news: the code is super duper gross

+

Lets spot a Pattern

+

The code is gross because we have these cascading blocks

+
case e1 of
+  Error err1 -> Error err1
+  Value v1   -> case e2 of
+                  Error err2 -> Error err2
+                  Value v1   -> Result (v1 + v2)
+

but really both blocks have something common pattern

+
case e of
+  Error err -> Error err
+  Value v   -> {- do stuff with v -}
+
    +
  1. Evaluate e
  2. +
  3. If the result is an Error then return that error.
  4. +
  5. If the result is a Value v then do further processing on v.
  6. +
+

Lets bottle that common structure in two functions:

+
    +
  • >>= (pronounced bind)
  • +
  • return (pronounced return)
  • +
+
+
Bottling a Magic Pattern
+
+
(>>=) :: Result a -> (a -> Result b) -> Result b
+(Error err) >>= _       = Error err
+(Value v)   >>= process = process v
+
+return :: a -> Result a
+return v = Value v
+

NOTE: return is not a keyword; it is just the name of a function!

+

A Cleaned up Evaluator

+

The magic bottle lets us clean up our eval

+
eval :: Expr -> Result Int
+eval (Number n)   = return n
+eval (Plus e1 e2) = eval e1 >>= \v1 ->
+                      eval e2 >>= \v2 ->
+                        return (v1 + v2)
+eval (Div e1 e2)  = eval e1 >>= \v1 ->
+                      eval e2 >>= \v2 ->
+                        if v2 == 0
+                          then Error ("yikes dbz:" ++ show e2)
+                          else return (v1 `div` v2)
+

The gross pattern matching is all hidden inside >>=

+

Notice the >>= takes two inputs of type:

+
    +
  • Result Int (e.g. eval e1 or eval e2)
  • +
  • Int -> Result Int (e.g. The processing function that takes the v and does stuff with it)
  • +
+

In the above, the processing functions are written using \v1 -> ... and \v2 -> ...

+

NOTE: It is crucial that you understand what the code above is doing, and why it is actually just a “shorter” version of the (gross) nested-case-of eval.

+

A Class for >>=

+

Like fmap or show or jval or ==, or <=, the >>= operator is useful across many types!

+

Lets capture it in an interface/typeclass:

+
class Monad m where
+  (>>=)  :: m a -> (a -> m b) -> m b
+  return :: a -> m a
+

Notice how the definitions for Result fit the above, with m = Result

+
instance Monad Result where
+  (>>=) :: Either a -> (a -> Either b) -> Either b
+  (Error err) >>= _       = Error err
+  (Value v)   >>= process = process v
+
+  return :: a -> Result a
+  return v = Value v
+

Syntax for >>=

+

In fact >>= is so useful there is special syntax for it.

+

Instead of writing

+
e1 >>= \v1 ->
+  e2 >>= \v2 ->
+    e3 >>= \v3 ->
+      e
+

you can write

+
do v1 <- e1
+   v2 <- e2
+   v3 <- e3
+   e
+...
+

Thus, we can further simplify our eval to:

+
eval :: Expr -> Result Int
+eval (Number n)   = return n
+eval (Plus e1 e2) = do v1 <- eval e1
+                       v2 <- eval e2
+                       return (v1 + v2)
+eval (Div e1 e2)  = do v1 <- eval e1
+                       v2 <- eval e2
+                       if v2 == 0
+                         then Error ("yikes dbz:" ++ show e2)
+                         else return (v1 `div` v2)
+









+

Generalizing Result to Many Values

+

We can generalize Result to “many” values, using List

+
data Result a = Err String | Result a
+data List   a = Nil        | Cons   a (List a) 
+
    +
  • The Err is like [] except it has a message too,
  • +
  • The tail of type (List a) lets us hold many possible a values
  • +
+

We can now make a Monad instance for lists as

+
instance Monad [] where
+  return = returnList 
+  (>>=)  = bindList 
+
+returnList :: a -> [a]
+returnList x = [x]
+
+bindList :: [a] -> (a -> [b]) -> [b]
+bindList []     f = []
+bindList (x:xs) f = f x ++ bindList xs f
+

Notice bindList xs f is like a for-loop:

+
    +
  • for each x in xs we call,
  • +
  • f x to get the results
  • +
  • and concatenate all the results
  • +
+

so,

+
bindList [x1,x2,x3,...,xn] f ==>
+  f x1 ++ f x2 ++ f x3 ++ ... ++ f xn
+

This has some fun consequences!

+
silly xs = do
+  x <- xs
+  return (x * 10)
+

produces the result

+
-- >>> silly [1,2,3]
+-- [10,20,30]
+

and

+
foo xs ys = do
+  x <- xs
+  y <- ys
+  return (x, y)
+

produces the result

+
-- >>> foo ["1", "2", "red", "blue"] ["fish"]
+-- [("1","fish"),("2","fish"),("red","fish"),("blue","fish")]
+

behaves like Python’s

+
for x in xs:
+  for y in ys:
+    yield (x, y)
+

EXERCISE

+

Fill in the blanks to implement mMap (i.e. map using monads)

+
mMap :: (a -> b) -> [a] -> [b]
+mMap f xs = do
+  _fixme
+

EXERCISE

+

Fill in the blanks to implement mFilter (i.e. filter using monads)

+
mFilter :: (a -> Bool) -> [a] -> [a]
+mFilter f xs = do
+  _fixme
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/lectures/08-prolog.html b/docs/lectures/08-prolog.html new file mode 100644 index 0000000..38763a4 --- /dev/null +++ b/docs/lectures/08-prolog.html @@ -0,0 +1,1596 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

Logic Programming

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

Logic Programming

+

Imperative/functional programming

+
    +
  • Program = Algorithm + Data Structures

  • +
  • Execute

  • +
+

Logic programming

+
    +
  • Program = Facts + Rules

  • +
  • Query

  • +
+










+

Example

+

Collection of Facts

+
    +
  • Carnitas is Mexican
  • +
  • isMexican(carnitas).
  • +
+

Collection of Rules

+
    +
  • All Mexican food is delicious
  • +
  • isDelicious(X) :- isMexican(X).
  • +
+

Query

+
    +
  • Hey Prolog! What is a delicious food ?
  • +
  • ?- isDelicious(Y)
  • +
+

Answer

+
    +
  • Carnitas!
  • +
+



+

You don’t “run” a Prolog program, you ask it questions!

+










+

Why Prolog?

+










+

Prolog History

+

1970s: developed for Artificial Intelligence

+
    +
  • original vision “expert systems”
  • +
+

Out came the whole field of declarative programming:

+
    +
  • Specify what you want

  • +
  • Not how to obtain result

  • +
  • Ideal for searching large space of results

  • +
+










+

Declarative Programming: Example

+

Flight search: Orbitz / Expedia / etc.

+

Collection of Facts

+
    +
  • Airports, Flights, Times, Durations, Costs
  • +
+

Collection of Rules

+
    +
  • If travel from A to B with price (P1) AND B to C with price (P2)
  • +
  • then travel from A to C with price (P1 + P2)
  • +
+

Queries

+
    +
  • What is cheapest flight from SAN to JFK with duration < 6 Hrs ?
  • +
+










+

Declarative Programming: Applications

+

Used heavily in many domains (together with statistical methods)

+
    +
  • Scheduling

    +
      +
    • travel, sports, …
    • +
  • +
  • Rule-based Anomaly detection

    +
      +
    • Credit card fraud
    • +
  • +
  • SQL (and similar DB Query Languages)

  • +
+

Many of these are inspired-by or are subsets of Prolog

+










+

Why Prolog?

+


+

Most importantly:

+

yet another new way to think about programming!

+


+

(Remember the goal of this class: open your mind)

+










+

Plan

+

Language

+
    +
  1. Terms
  2. +
  3. Facts
  4. +
  5. Queries +
      +
    • implementation: unification
    • +
  6. +
  7. Rules
  8. +
  9. Programs +
      +
    • implementation: backtracking search
    • +
  10. +
+

Programming

+
    +
  • Numeric Computation
  • +
  • Data Structures
  • +
  • Puzzle Solving
  • +
+










+

Language: Terms

+

Prolog Program

+
    +
  • Facts
  • +
  • Rules
  • +
  • Queries
  • +
+

about what ? Terms!

+



+

Syntax of Prolog terms:

+
t ::= n                -- number: 1, 92, 4.4
+    | a                -- atom
+    | X                -- variable
+    | a (t1, t2, ..)   -- compound term
+

where

+
    +
  • a is an identifier that starts with a lower-case letter
  • +
  • X is an identifier that starts with an upper-case letter
  • +
+









+

Prolog Terms: Atoms

+

Atom: any identifier starting with lower-case

+

Examples: x, alice, taco, giraffe, appleSauce

+



+

Prolog knows NOTHING about the atoms, except that:

+
    +
  • Each atom is equal to itself +
      +
    • alice = alice
    • +
    • taco = taco
    • +
  • +
  • Each atem is disequal to every other atom +
      +
    • alice = taco never holds
    • +
  • +
+



+

You can think of atoms as constructors of a single mega enum type:

+
    +
  • data Atom = x | alice | taco | giraffe | appleSauce | ...
  • +
  • except that in Haskell, constructors start with upper case :(
  • +
+









+

Prolog Terms: Variables

+

Variable: any identifier starting with upper-case

+
    +
  • Examples: X, Y, Z, Head, Tail, Taco, Burrito, Alice, Bob

  • +
  • _ is the wildcard variable, similar to Haskell

  • +
+



+

A variable stands for (and can be substituted with) any term:

+
    +
  • alice = taco never holds
  • +
  • but X = taco holds is we replace [X = taco]
  • +
  • what about Alice = taco?
  • +
+









+

Prolog Terms: Compound Terms

+

Compound terms are of the form

+

a (t1, t2, t3, ..)

+

where

+
    +
  • a is a lower-case string (a function symbol)
  • +
  • each ti is a term (an argument to the function symbol)
  • +
+

Examples:

+
x(y, z)              % y, z       are atoms
+parent(alice, bob)   % alice, bob are atoms
+parent(alice, Child) % alice is an atom, Child is a variable
+

You can think of compound terms as trees:

+

+

They are not function calls!

+









+

Prolog terms: Haskell representation

+

If we were implementing a Prolog interpreter in Haskell, we would represent terms like so:

+
data Term
+  = Num Int
+  | Atom LId
+  | Var UId
+  | Compound LId [Term]
+  
+type LId = String -- lower-case
+type UId = String -- upper-case
+









+

QUIZ

+
data Term
+  = Num Int
+  | Atom LId
+  | Var UId
+  | Compound LId [Term]
+

What Haskell value of type Term represents Prolog term parent(alice, Bob) ?

+

A. parent ("alice", "Bob")

+

B. parent (Atom "alice") (Var "Bob")

+

C. [Atom "parent", Atom "alice", Var "Bob"]

+

D. Compound "parent" [Atom "alice", Var "Bob"]

+

E. Compound (Atom "parent") [Atom "alice", Var "Bob"]

+


+








+

QUIZ

+
data Term
+  = Num Int
+  | Atom LId
+  | Var UId
+  | Compound LId [Term]
+

What Haskell value of type Term represents Prolog term factorial(5) ?

+

A. factorial(5)

+

B. factorial(Atom 5)

+

C. 120

+

D. Num 120

+

E. Compound "factorial" [Num 5]

+


+








+

Prolog Terms

+

Prolog term factorial(5) is simply the tree

+

+

Prolog has no idea what factorial is (it’s just a name)

+









+

Plan

+

Language

+
    +
  1. Terms
  2. +
  3. Facts
  4. +
  5. Queries
  6. +
  7. Rules
  8. +
  9. Programs
  10. +
+

Programming

+
    +
  • Numeric Computation
  • +
  • Data Structures
  • +
  • Puzzle Solving
  • +
+










+

Language: Facts

+

A fact is a “boolean” term (typically without variables) followed by .

+

Examples:

+

The following facts specify a list of parent-child relationships:

+
parent(kim, holly).  
+parent(margaret, kim).  
+parent(herbert, margaret).
+parent(john, kim).
+parent(felix, john).  
+parent(albert, felix).
+parent(albert, dana).
+parent(felix, maya).  
+
                  albert
+                  |    |
+herbert        felix    dana
+    |         |    |
+  margaret  john   maya
+         |  |
+         kim
+          |    
+        holly
+

Here:

+
    +
  • kim, holly, margaret etc. are all atoms
  • +
  • parent is a predicate (i.e. a boolean function symbol)
  • +
  • … of arity 2 (i.e. takes two arguments)
  • +
+



+

Predicates have no meaning to Prolog, but the programmer mentally notes that:

+
    +
  • parent(kim, holly) means kim is a “parent-of” holly
  • +
  • parent(margaret, kim) means margaret is a “parent-of” kim
  • +
  • etc.
  • +
+










+

Database of facts

+

Prolog maintains a database (collection) of facts

+

Suppose we have a collection of facts saved in intro.pl

+

You can load the facts into the Prolog interpreter

+
?- consult('intro.pl').
+true.
+

… or you can add them one-at-a-time

+
?- assert(parent(margaret, kim)).
+

Once facts are loaded, you query Prolog

+
    +
  • “Hey Prolog! Is this new fact in your Database … or can it be inferred from your database?”
  • +
+










+

Plan

+

Language

+
    +
  1. Terms
  2. +
  3. Facts
  4. +
  5. Queries (Implementation: Unification)
  6. +
  7. Rules
  8. +
  9. Programs (Implementation: Backtracking Search)
  10. +
+

Programming

+
    +
  • Numeric Computation
  • +
  • Data Structures
  • +
  • Puzzle Solving
  • +
+










+

Language: Query

+

A query is a boolean term (possibly with variables), which we type into a prompt

+



+

Example 1:

+
?- parent(margaret, kim).
+

Prolog replies:

+
true.
+
    +
  • As this was indeed one of the facts loaded in intro
  • +
+



+

Example 2:

+
?- parent(margaret, john).  
+

What does Prolog reply? Why?

+





+

The meaning of the query is “Is this fact provable”?

+

So the answer is: “No”

+



+

Pfft. Big deal? Is Prolog just a table lookup?!

+










+

Queries With Variables

+

What should Prolog reply to this query?

+
?- parent(margaret, X).
+







+

Meaning:

+

“Hey Prolog, for which value(s) of X is the fact provable”?

+

Prolog replies:

+
X = kim.
+
    +
  • Because it can plug-in kim for X,
  • +
  • and then can infer parent(margaret, kim) from the facts
  • +
+










+

More examples

+

Example 3:

+
?- parent(X, kim).
+

Meaning: ???

+


+

Prolog replies: ???

+



+

Example 4:

+
?- parent(X, Y).
+

Meaning: ???

+


+

Prolog replies: ???

+










+

QUIZ

+

How do we ask Prolog the following query:

+
+

Does there exist any person who is their own parent ?

+
+

A. parent(kim, kim)

+

B. parent(x, x)

+

C. parent(X, X)

+

D. parent(X, Y)

+

E. parent(Y, X)

+


+









+

More examples

+

Example 4:

+

How do we ask Prolog:

+
+

Does there exist any person who has more than one child?

+
+










+

Queries work like magic

+

In Java/C#/Haskell you would need

+
    +
  • Some parentOf or childOf data structure / methods +
      +
    • to represent parent-child relationship
    • +
  • +
  • Some looping or iteration +
      +
    • to search through all pairs
    • +
  • +
  • Instead, Prolog uses facts and queries +
      +
    • to search forwards and backwards
    • +
    • to enumerate all results
    • +
    • in a single uniform declarative manner!
    • +
  • +
+


+

How?

+


+

Magic = Unification + Backtracking Search

+










+

Plan

+

Language

+
    +
  1. Terms
  2. +
  3. Facts
  4. +
  5. Queries +
      +
    • implementation: unification
    • +
  6. +
  7. Rules
  8. +
  9. Programs +
      +
    • implementation: backtracking search
    • +
  10. +
+

Programming

+
    +
  • Numeric Computation
  • +
  • Data Structures
  • +
  • Puzzle Solving
  • +
+










+

Unification

+

When does one term match (= unify with) another?

+






+

A term unifies with another term when we can substitute values for their variables to make them identical

+



+

Unification is the computational heart of Prolog

+


+

When you write

+
?- term1 = term2.
+

you are asking whether term1 can be unified with term2.

+










+

Unification By Example

+
?- kim = kim.
+true.
+% atom is identical to itself!
+
+?- kim = holly.
+false. 
+% different atoms are never identical
+
+?- foo(kim) = foo(kim).
+true.
+% compound term: recursively unify subtrees
+
+?- foo(kim) = foo(holly).
+???
+
+?- X = kim.
+X = kim.
+% X can be made identical to kim if we substitute [X = kim]
+
+?- X = foo(kim).
+???
+
+?- foo(X) = foo(kim).
+???
+
+?- X = Y.
+???
+










+

QUIZ

+

How does Prolog respond to the following query?

+
?- p(X, dog) = p(cat, Y).
+

A. false

+

B. X = cat, Y = cat.

+

C. X = dog, Y = dog.

+

D. X = dog, Y = cat.

+

E. X = cat, Y = dog.

+


+









+

QUIZ

+

How does Prolog respond to the following query?

+
?- p(X, dog, X) = p(cat, Y, Y).
+

A. false

+

B. X = cat, Y = cat.

+

C. X = dog, Y = dog.

+

D. X = dog, Y = cat.

+

E. X = cat, Y = dog.

+


+









+

Unification step-by-step

+

Let’s see how Prolog reasons about this query:

+
?- p(X, dog, X) = p(cat, Y, Y).
+

Two compound terms: function symbols match, so recursively unify arguments…

+

+




+

To unify X and cat, extend the current substitution with X = cat

+

+




+

Apply substitution [X = cat] to both terms. Move on to next leaf…

+

+




+

To unify dog and Y, extend the current substitution with Y = dog

+

+




+

… and apply this substitution throughout both terms.

+

+




+

Uh oh! Now we have different atoms in the last leaf! Unification fails.

+

+










+

Unification in Prolog vs Nano?

+

It’s the same thing, except!

+ + + + + + + + + + + + + + + + + +
Unification in NanoUnification in Prolog
on two typeson two terms
is a pain to implementis built-in
+



+

So, if we implemented Nano type inference in Prolog instead of Haskell, we wouldn’t have to implement unify, we could just use =!

+
?- int = int.
+true.
+
+?- int = bool.
+false. 
+
+?- funT(int,A) = funT(B,bool).
+A = bool, B = int.
+
+?- A = funT(A,A).
+???
+










+

Unification for answering queries

+

What happens when we ask

+
?- parent(margaret, X).  
+
    +
  • Prolog goes through the list of facts in its database

  • +
  • For each fact, checking if it can be unified with the query

    +
      +
    • If unification fails it moves on to the next fact

    • +
    • If unification succeeds it returns the unifier (the unifying substitution)

      +
        +
      • if the unifier is empty, it just replies true
      • +
    • +
  • +
  • If unification fails for each fact, it relies false

  • +
+




+

More examples

+
?- parent(X, kim).  
+

This query has multiple solutions!

+
    +
  • Once the first solution is found, we can ask Prolog to keep going through the facts
  • +
+



+
?- parent(X, Y).  
+










+

Plan

+

Language

+
    +
  1. Terms
  2. +
  3. Facts
  4. +
  5. Queries +
      +
    • implementation: unification
    • +
  6. +
  7. Rules
  8. +
  9. Programs +
      +
    • implementation: backtracking search
    • +
  10. +
+

Programming

+
    +
  • Numeric Computation
  • +
  • Data Structures
  • +
  • Puzzle Solving
  • +
+










+

Complex queries

+

How do we determine if margaret is the grandparent of holly?

+






+

We use a conjunction (comma-separated sequence of terms):

+
?- parent(margaret, X), parent(X, holly).
+

Is there a person X who is a child of margaret AND a parent of holly ?

+

Note: how we link the terms with the same variable to capture relationships.

+



+

How do we determine who is the great-grandparent of holly?

+






+
?- parent(GGP, GP), parent(GP,P), parent(P, holly).
+










+

QUIZ

+

Which of these queries returns all pairs X, Y of (half-) siblings?

+

A. parent(X, Y), X \= Y.

+

B. parent(X, Z), parent(Z, Y), X \= Y.

+

C. parent(X, Z), parent(Y, Z), X \= Y.

+

D. parent(Z, X), parent(Z, Y), X \= Y.

+


+









+

Complex queries

+
?- parent(GGP, GP), parent(GP,P), parent(P, holly).
+

This is getting a tad repetitive!

+

What do we do in other languages to avoid repeating ourselves?

+





+

Abstract things into functions!

+

In Prolog, they are called rules

+










+

Rules

+

Rules let us build complex predicates from queries

+


+

Syntax:

+
t :- t1, t1, t2,...
+
    +
  • t is called the head of the rule
  • +
  • t1, t2, t3,... is called the body of the rule
  • +
  • all t, t1, t2, t3,... are predicate applications
  • +
+


+

Intuition:

+

To prove the head t, you must prove all sub-goals t1 AND t2 AND t3

+


+

Example:

+

We can build a new (complex) predicate grandparent from parent predicates:

+
grandparent(GP, GC) :- parent(GP, P), parent(P, GC).
+

GP is a grandparent of GC if GP is a parent of P and P is a parent of GC

+





+

Now we can use the new predicate in a query:

+
?- grandparent(X, kim). % who are the grandparents of kim
+X = herbert ;           % hit ; to see next
+X = felix   ;           % hit ; to see next
+false.                  % thats it!
+





+

Why? Because Prolog can prove the sub-goals:

+
?- parent(herbert, P), parent(P, kim).  % Solution 1. X = herbert
+P = margaret.
+
+?- parent(felix, P), parent(P, kim).    % Solution 2. X = felix
+P = john .
+










+

QUIZ

+

Which of the following is a valid greatgrandparent predicate?

+

A. greatgrandparent(X, Y) :- parent(X, Y), grandparent(X, Y).

+

B. greatgrandparent(X, Y) :- parent(X, Z), grandparent(Z, Y).

+

C. greatgrandparent(X, Y) :- grandparent(X, Z), parent(Z, Y).

+

D. greatgrandparent(X, Y) :- parent(X, Z), parent(Z, Y).

+

E. greatgrandparent(X, Y) :- parent(X, Z1), parent(Z1, Z2), parent(Z2, Y).

+


+









+

Rules: complex predicates from queries

+
greatgrandparent(GGP, GC) :- parent(GGP, GP), grandparent(GP, GC).
+

That was your first Prolog program!

+

Let’s take it out for a spin!

+
?- greatgrandparent(X, holly).
+X = herbert ;
+X = felix ;
+false.
+










+

Plan

+

Language

+
    +
  1. Terms
  2. +
  3. Facts
  4. +
  5. Queries +
      +
    • implementation: unification
    • +
  6. +
  7. Rules
  8. +
  9. Programs +
      +
    • implementation: backtracking search
    • +
  10. +
+

Programming

+
    +
  • Numeric Computation
  • +
  • Data Structures
  • +
  • Puzzle Solving
  • +
+










+

Prolog Programs

+

A program is a collection of facts and rules

+

Facts and Rules are two kinds of clauses:

+
    +
  • Fact: clause without any conditions

  • +
  • Rule: clause with conditions

    +
      +
    • used to derive new facts
    • +
  • +
+










+

Program structure

+

How do combine multiple rules into a program?

+
    +
  1. Scope

  2. +
  3. Disjunction

  4. +
  5. Recursion

  6. +
+










+

Scope

+

In the grandparent rule, the variable GP appears twice:

+
greatgrandparent(GGP, GC) :- parent(GGP, GP), grandparent(GP, GC).
+

Is it the same GP?

+





+

Below we use X in both rules:

+
isDelicious(X)  :- isMexican(X).
+isVegetarian(X) :- isVegan(X).
+

Is is the same X?

+





+

In Prolog, the scope of a variable is the single rule where it appears.

+

There is no connection between variables across rules.

+










+

Program structure

+

How do combine multiple rules into a program?

+
    +
  1. Scope

  2. +
  3. Disjunction

  4. +
  5. Recursion

  6. +
+










+

Complex Predicates: Disjunction

+

Lets write a predicate has_family which is true for persons who

+
    +
  • either have a parent

  • +
  • or have a child

  • +
+






+
has_family(X) :- parent(X, _). % if X is the parent of some _
+has_family(X) :- parent(_, X). % if X is the child of some _
+

_ is a wildcard or dont-care variable (as in Haskell)

+


+

Intuition:

+
    +
  • If we can prove parent(X, _) then we can prove has_family(X)

  • +
  • And also: if we can prove parent(_, X) then we can prove has_family(X)

  • +
+


+

Running the program:

+
?- has_family(holly).
+true.  % Second rule fires for holly
+
+?- has_family(sansa).
+false. % Neither rule fires for sansa
+










+

Complex Predicates: Disjunction

+
has_family(X) :- parent(X, _). % if X is the parent of some _
+has_family(X) :- parent(_, X). % if X is the child of some _
+

Can be abbreviated to

+
has_family(X) :- parent(X, _) ; parent(_, X).
+

Semicolon ; indicates disjunction.

+










+

Program structure

+

How do combine multiple rules into a program?

+
    +
  1. Scope

  2. +
  3. Disjunction

  4. +
  5. Recursion

  6. +
+










+

Recursion

+

Lets write a predicate ancestor(A, X) which is true if

+
    +
  • parent(A, X)or

  • +
  • parent(A, P) and parent(P, X)or

  • +
  • parent(A, GP) and parent(GP, P) and parent(P, X)or

  • +
  • … if some chain of parent-links holds between A and X.

  • +
+




+

A is an ancestor of X if:

+
    +
  • Base case: A is a parent of X

    +

    ancestor(A, X) :- parent(A, X).

  • +
  • Inductive case: A is an ancestor of X’s parent

    +

    ancestor(A, X) :- parent(P, X), ancestor(A, P).

  • +
+





+

To sum up:

+
ancestor(A, X) :- parent(A, X).
+ancestor(A, X) :- parent(P, X), ancestor(A, P).
+

What can we do with this predicate?

+





+

We can find descendants (forwards)

+
?- ancestor(kim, X).
+X = holly.
+





+

We can find ancestors (backwards)

+
?- ancestor(X,kim).
+X = margaret  ;
+X = john      ;
+X = herbert   ;
+X = felix     ;
+X = albert    .
+






+

Pretty neat: search forward or back, in just two lines!

+
    +
  • Try doing that in any other language!
  • +
+










+

Plan

+

Language

+
    +
  1. Terms
  2. +
  3. Facts
  4. +
  5. Queries +
      +
    • implementation: unification
    • +
  6. +
  7. Rules
  8. +
  9. Programs +
      +
    • implementation: backtracking search
    • +
  10. +
+

Programming

+
    +
  • Numeric Computation
  • +
  • Data Structures
  • +
  • Puzzle Solving
  • +
+










+ +

How does Prolog answer recursive queries?

+
    +
  • Brute force backtracking search
  • +
+

View each clause as a proof rule:

+
goal :- subgoal_1, subgoal_2, ...
+goal :- subgoal_3, subgoal_4, ...
+...
+

To prove goal, try to:

+
    +
  1. Prove subgoal_1 and then prove subgoal_2 etc, or, failing that,
  2. +
  3. Prove subgoal_3 and then prove subgoal_4, etc, or, failing that
  4. +
+



+

Example:

+
ancestor(A,X) :- parent(A,X).
+ancestor(A,X) :- parent(P,X), ancestor(A,P).
+

To prove ancestor(A,X), try to:

+
    +
  1. Prove parent(A,X), or, failing that,
  2. +
  3. Prove parent(P,X), and then prove ancestor(A,P).
  4. +
+



+

Suppose we ask it the query:

+
?- ancestor(felix,holly).
+

To prove this query, it undertakes the following backtracking search:

+
    ancestor(felix,holly)?
+      /                    \
+parent(felix,holly)    parent(P0,holly)
+  NO                   ancestor(felix,P0)
+                       |
+      ------------------
+      |
+      | P0=kim  (by fact)
+      |
+      ancestor(felix,kim)
+      /                \
+  parent(felix,kim)     parent(P1,kim)
+      NO                ancestor(felix,P1)
+                         |              |
+           P1 = margaret |              | P1 = john
+                         |              |                         
+        ancestor(felix,margaret)        ancestor(felix,john)
+                /        \                        |
+parent(felix,margaret)   parent(P2,margaret)   parent(felix,john)
+            NO           ancestor(felix,P2)         YES
+                                    |
+                       P2 = herbert |
+                                    |
+                      ancestor(felix, herbert)
+                     /              |
+   parent(felix,herbert)   parent(P3,herbert)
+              NO                   NO
+



+

NOTE: Prolog introduces a fresh P variable (P1, P2, P3...) every time the second rule for ancestor is triggered

+



+

HINT: Trace mode in Prolog shows the search tree!

+
?- trace.
+?- ancestor(felix,holly).
+










+

Queries with Variables

+

Backtracking Search is done for every query.

+
  ?- ancestor(X,kim).
+
    +
  • Prolog does the proof search
  • +
  • Returns all unifiers for X for which the proof succeeds with YES.
  • +
+










+

Order matters

+

Order of clauses & terms influences unification & backtracking.

+

For each

+
    +
  • goal different clauses are selected in order
  • +
  • clause subgoals are solved (and unified!) from left-to-right
  • +
+










+

QUIZ

+

Which alternative definitions of ancestor are correct?

+
% A
+ancestor(A,X) :- parent(P,X), ancestor(A,P).
+ancestor(A,X) :- parent(A,X).
+
+% B
+ancestor(A,X) :- parent(A,X).
+ancestor(A,X) :- ancestor(A,P), parent(P,X).
+
+% C
+ancestor(A,X) :- ancestor(A,P), parent(P,X).
+ancestor(A,X) :- parent(A,X).
+
+% D: All of the above
+
+% E: None of the above
+


+









+

Bad orders

+

A bad order of sub-goals of in a recursive rule can cause non-termination

+

If we define:

+
ancestor(A,X) :- ancestor(A,P), parent(P,X).
+ancestor(A,X) :- parent(A,X).
+

The following query doesn’t terminate:

+
?- ancestor(felix,holly).
+



+

Why? The search tree looks like this now!

+
  ancestor(felix,holly)?
+    |
+    |
+    |
+  ancestor(felix,P0)  %prove first subgoal,
+    |                 %then parent(P,holly)
+    |
+    |
+  ancestor(felix,P1)  %prove first subgoal,
+    |	                %then parent(P1,P0)
+    |
+    |
+  ancestor(felix,P2)
+    .
+    .
+    .
+

To avoid infinite recursion:

+
    +
  • Always place the base-case subgoal first in a recursive rule

  • +
  • Then unification with the base facts fixes the variable P

  • +
  • Thereby guaranteeing termination

  • +
+










+

QUIZ

+

What is the best definition of sibling?

+
    +
  • where sibling(X, Y) if X and Y share a parent.
  • +
+
% A
+sibling(X, Y) :- parent(P, X), parent(P, Y).
+
+% B
+sibling(X, Y) :- parent(P, X), parent(P, Y), not(X = Y).
+
+% C
+sibling(X, Y) :- not(X = Y), parent(P, X), parent(P, Y).
+


+









+

Ordering and Unification

+

Remember: = means unification!

+

So, X = Y always holds:

+
?- X = Y.
+X = Y
+

Hence, not(X=Y) (which is the same as X \= Y) always fails!

+
?- not(X = Y).
+false.
+

So, if we define:

+
sibling(X, Y) :- not(X = Y), parent(P, X), parent(P, Y).
+

Then sibling(X,Y) (with variables for inputs) always fails!

+



+

Solution

+

Always ensure the sub-goal not(X=Y) fires after X and Y have been fully instantiated:

+



+
sibling(X, Y) :- parent(P, X), parent(P, Y), not(X = Y).
+

and now we get:

+
    ?- sibling(X,Y).
+    X = john
+    Y = maya ;
+
+    X = felix
+    Y = dana ;
+
+    X = dana
+    Y = felix ;
+
+    X = maya
+    Y = john ;
+    No
+










+

Plan

+

Language

+

Programming

+
    +
  • Numeric Computation
  • +
  • Data Structures
  • +
  • Puzzle Solving
  • +
+










+

Numeric Computation

+

Two big problems:

+
    +
  1. How do we even evaluate? e.g. 2 + 3 ?

  2. +
  3. How do we write functions? e.g. add x y = x + y

  4. +
+










+

Problem 1: How to Evaluate?

+

Everything is a term and = is a unification operator:

+
?- X = 2 + 3.
+X = 2 + 3
+

But to “compute” we need to be able to evaluate 2 + 3 to 5!

+



+

Prolog has a magic operator called is that evaluates its second argument:

+
?- X is 2 + 3.
+X = 5.
+



+

To solve the goal t1 is t2, Prolog:

+
    +
  1. evaluates t2 and then
  2. +
  3. unifies result with t1
  4. +
+



+

All variables in t2 must be instantiated before evaluation!

+
% Not good:
+?- Y is X+2, X=1.
+ERROR: Args are not sufficiently instantiated
+
+% This is okay:
+?- X=1, Y is X+2.
+X=1
+Y=3
+










+

Problem 2: How to Write Functions?

+

In Prolog we can define predicates using clauses (facts and rules)

+

Can we represent a function definition like

+
add x y = x + y
+

using predicates and clauses?

+










+

QUIZ

+

Which of the following represents add x y = x + y?

+
% A
+addP(X, Y) :- Z is X + Y.
+
+% B
+addP(X, Y, Z) :- Z is X + Y.
+
+% C
+addP(X, Y, X + Y).
+
+% D
+addP(X, Y) :- X + Y.
+
+% E
+addP(X, Y, Z) :- X + Y is Z.
+


+









+

Functions as Predicates

+

Every function of the form:

+
foo x y = Res
+

corresponds to a predicate of the form:

+
fooP(X, Y, Res).
+

i.e. a predicate that is True for those triples (X, Y, Res) s.t. the function foo X Y evaluated to Res

+

The predicate captures the input-output relation of the function.

+










+

Example: Fibonacci

+

Recall the definition of Fibonacci numbers in Haskell:

+
fib :: Int -> Int
+fib 0 = 1
+fib 1 = 1
+fib n = fib (n - 1) + fib (n -2)
+


+

Lets write a Prolog predicate capturing the IO relationship of fib:

+
fib(N, Res)
+

holds only when Res is the N-th Fibonacci number.

+

When we are done, we can call the function with a query:

+
?- fib(0, Res).
+Res = 1
+
+?- fib(5, Res).
+Res = 8
+










+

QUIZ

+

What is the correct Prolog implementation of factorial?

+
% A
+fact(1,1). 
+fact(N,R) :- N1 is N-1, fact(N1,R1), R is N*R1.
+
+% B
+fact(N,1) :- N < 2.
+fact(N,R) :- N1 is N-1, fact(N1,R1), R is N*R1.
+
+% C
+fact(N,1) :- N < 2.
+fact(N,R) :- 1 < N, N1 is N-1, fact(N1,R1), R is N*R1.
+
+% D
+fact(N,R) :- 1 < N, N1 is N-1, fact(N1,R1), R is N*R1.
+fact(N,1) :- N < 2.
+


+









+

Plan

+

Language

+

Programming

+
    +
  • Numeric Computation
  • +
  • Data Structures
  • +
  • Puzzle Solving
  • +
+










+

Lists

+

Prolog has built-in syntax for lists

+
?- [H|T] = [1,2,3].
+H = 1,
+T = [2,3].
+


+

Feels a lot like Haskell pattern matching

+
    +
  • no wonder: pattern matching is unification!
  • +
+


+

Unlike Haskell, can mix , and |:

+
?- [X,Y|T] = [1,2,3].
+X = 1,
+Y = 2,
+T = [3].
+





+

Head and tail

+

We have to write them as predicates:

+
headOf([H|_],H).
+
+tailOf([_|T],T).
+










+

QUIZ

+

How do we write a predicate hasThreeOrMore that satisfies the following test cases:

+
?-hasThreeOrMore([]).
+false.
+?-hasThreeOrMore([1,two]).
+false.
+?-hasThreeOrMore([1,two,dog]).
+true.
+?-hasThreeOrMore([1,two,dog,taco]).
+true.
+

A. hasThreeOrMore([_|_]).

+

B. hasThreeOrMore([_,_|_]).

+

C. hasThreeOrMore([_,_,_|_]).

+

D. hasThreeOrMore([_,_,_,_|_]).

+

E. hasThreeOrMore([_,_,_]).

+


+









+

Recursive functions on lists

+

Example 1: isIn

+

Let’s write the Prolog equivalent of the following Haskell function:

+
isIn :: a -> [a] -> Bool
+isIn _ []     = False
+isIn x (y:ys) = x == y || isIn x ys
+






+
isIn(X,[X|_]).
+isIn(X,[_|T]) :- isIn(X,T).
+






+

Example 2: sum

+

Let’s write the Prolog equivalent of the following Haskell function:

+
sum :: [Int] -> Int
+sum []     = 0
+sum (x:xs) = x + sum xs
+






+
sum([],0).
+sum([H|T],R) :- sum(T,R1), R is R1 + H.
+






+

Example 3: append

+

Let’s write the Prolog equivalent of the following Haskell function:

+
append :: [a] -> [a] -> [a]
+append []     ys = ys
+append (x:xs) ys = x:(append xs ys)
+






+
append([],Ys,Ys).
+append([H|T],Ys,[H|R]) :- append(T,Ys,R).
+



+

Unlike in Haskell, Prolog’s append is magical!

+
?- append(Xs,Ys,[1,2,3,4]).
+Xs = [], Ys = [1,2,3,4] ;
+Xs = [1], Ys = [2,3,4]  ;
+Xs = [1,2], Ys = [3,4]  ;
+Xs = [1,2,3], Ys = [4]  ;
+Xs = [1,2,3,4], Ys = [] .
+



+

Prolog has a built-in library of list functions, including append, reverse, etc

+










+

Plan

+

Language

+

Programming

+
    +
  • Numeric Computation
  • +
  • Data Structures
  • +
  • Puzzle Solving
  • +
+










+

Farmer, Wolf, Goat, Cabbage

+
+
The real puzzle
+
+

Farmer needs to end up on the east bank of the river.

+

He has a little boat that can only carry him and one of the goods.

+

Goat can’t be left alone with the cabbage, wolf can’t be left alone with the goat.

+

Let’s ask Prolog to solve it for us!

+



+

We will encode the state of the game as a list with four positions:

+
[P_Farmer,P_Wolf,P_Goat,P_Cab]
+

Each position can take on one of the two atoms: east or west.

+



+

First, we define four legal moves that have the farmer at at most one good in the boat:

+
move([X,X,P_Goat,P_Cab],     move_wolf,   [Y,Y,P_Goat,P_Cab])      
+  :- change(X,Y).
+move([X,P_Wolf,X,P_Cab],     move_goat,   [Y,P_Wolf,Y,P_Cab])      
+  :- change(X,Y).
+move([X,P_Wolf,P_Goat,X],    move_cabbage,[Y,P_Wolf,P_Goat,Y])     
+  :- change(X,Y).
+move([X,P_Wolf,P_Goat,P_Cab],move_nothing,[Y,P_Wolf,P_Goat,P_Cab]) 
+  :- change(X,Y).
+
+change(east,west).
+change(west,east).
+



+

Next, we define the safety condition:

+
safe([P_Farmer,P_Wolf,P_Goat,P_Cab]) :-
+  one_equal(P_Farmer,P_Wolf,P_Goat),
+  one_equal(P_Farmer,P_Goat,P_Cab).
+  
+one_equal(X,X,_).
+one_equal(X,_,X).
+



+

Finally, we define a solution as a sequence of legal moves that ends in the final state:

+
solution([east,east,east,east],[]).
+solution(State,[FirstMove|RemainingMoves]) :-
+  move(State,FirstMove,NextState),
+  safe(NextState),
+  solution(NextState,RemainingMoves).
+



+

Now we can ask Prolog to solve the puzzle like so:

+
?- length(Moves,7), solution([west,west,west,west],Moves).
+



+

Why do we need length(Moves,7)?

+










+

Backtracking control

+

Sometimes we want more fine-grained control over the backtracking

+


+

For example:

+
?- length(Moves,7), solution([west,west,west,west],Moves).
+

gives us a gazillion solutions, most that all look the same (-ish?)

+




+

Stop backtracking

+

The cut operator ! is a goal that

+
    +
  • always succeeds
  • +
  • cannot be back-tracked out of
  • +
+



+

We can use cut to tell Prolog: once you found a solution to the puzzle, look no further!

+
solution([east,east,east,east],[]).
+solution(State,[FirstMove|RemainingMoves]) :-
+  move(State,FirstMove,NextState),
+  safe(NextState),
+  solution(NextState,RemainingMoves), !.
+





+

Collect all solutions

+

Sometimes you want to localize the backtracking and collect all solutions in a list.

+
?- isIn(X,[2,1,3,2]).
+X = 2.
+X = 1.
+X = 3.
+X = 2.
+



+

Use bagof to get the list of all solutions:

+
?- bagof(X, isIn(X,[2,1,3,2]), Sols).
+Sols = [2,1,3,2].
+



+

Use setof to get the list of all unique solutions:

+
?- setof(X, isIn(X,[2,1,3,2]), Sols).
+Sols = [1,2,3].
+



+

How do we find all unique solutions to the farmer puzzle?

+










+

That’s all folks!

+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/lectures/09-io.html b/docs/lectures/09-io.html new file mode 100644 index 0000000..1c72628 --- /dev/null +++ b/docs/lectures/09-io.html @@ -0,0 +1,552 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

Hello, world! (The IO Monad)

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

Writing Applications

+

In most language related classes, we start with a “Hello world!” program.

+

With 130, we will end with it.

+












+ +

Purity and the Immutability Principle

+

Haskell is a pure language. Not a value judgment, but a precise technical statement:

+

The “Immutability Principle”:

+
    +
  • A function must always return the same output for a given input

  • +
  • A function’s behavior should never change

  • +
+









+

No Side Effects

+

+

Haskell’s most radical idea: expression ==> value

+
    +
  • When you evaluate an expression you get a value and nothing else happens
  • +
+

Specifically, evaluation must not have an side effects

+
    +
  • change a global variable or

  • +
  • print to screen or

  • +
  • read a file or

  • +
  • send an email or

  • +
  • launch a missile.

  • +
+









+

Purity

+

Means functions may depend only on their inputs

+
    +
  • i.e. functions should give the same output for the same input every time.
  • +
+









+

But… how to write “Hello, world!”

+

But, we want to …

+
    +
  • print to screen
  • +
  • read a file
  • +
  • send an email
  • +
+

A language that only lets you write factorial and fibonacci is … not very useful!

+

Thankfully, you can do all the above via a very clever idea: Recipe

+









+

Recipes

+

This analogy is due to Joachim Brietner

+

Haskell has a special type called IO – which you can think of as Recipe

+
type Recipe a = IO a
+

A value of type Recipe a is

+
    +
  • a description of an effectful computations

  • +
  • when when executed (possibly) perform some effectful I/O operations to

  • +
  • produce a value of type a.

  • +
+












+

Recipes have No Effects

+

A value of type Recipe a is

+
    +
  • Just a description of an effectful computation

  • +
  • An inert, perfectly safe thing with no effects.

  • +
+
+
Cake vs. Recipe
+
+

(L) chocolate cake, (R) a sequence of instructions on how to make a cake.

+

They are different (hint: only one of them is delicious.)

+

Merely having a Recipe Cake has no effects: holding the recipe

+
    +
  • Does not make your oven hot

  • +
  • Does not make your your floor dirty

  • +
+












+

Executing Recipes

+

There is only one way to execute a Recipe a

+

Haskell looks for a special value

+
main :: Recipe ()
+

The value associated with main is handed to the runtime system and executed

+
+
Baker Aker
+
+

The Haskell runtime is a master chef who is the only one allowed to cook!

+












+

How to write an App in Haskell

+

Make a Recipe () that is handed off to the master chef main.

+
    +
  • main can be arbitrarily complicated

  • +
  • will be composed of many smaller recipes

  • +
+












+

Hello World

+
putStrLn :: String -> Recipe ()
+

The function putStrLn

+
    +
  • takes as input a String
  • +
  • returns as output a Recipe ()
  • +
+

putStrLn msg is a Recipe () when executed prints out msg on the screen.

+
main :: Recipe ()
+main = putStrLn "Hello, world!"
+

… and we can compile and run it

+
$ ghc --make hello.hs
+$ ./hello
+Hello, world!
+












+

QUIZ: Combining Recipes

+

Next, lets write a program that prints multiple things:

+
main :: IO ()
+main = combine (putStrLn "Hello,") (putStrLn "World!")
+
+-- putStrLn :: String -> Recipe ()
+-- combine  :: ???
+

What must the type of combine be?

+
{- A -} combine :: () -> () -> ()
+{- B -} combine :: Recipe () -> Recipe () -> Recipe ()
+{- C -} combine :: Recipe a  -> Recipe a  -> Recipe a
+{- D -} combine :: Recipe a  -> Recipe b  -> Recipe b
+{- E -} combine :: Recipe a  -> Recipe b  -> Recipe a
+
















+

Using Intermediate Results

+

Next, lets write a program that

+
    +
  1. Asks for the user’s name using
  2. +
+
    getLine :: Recipe String
+
    +
  1. Prints out a greeting with that name using
  2. +
+
    putStrLn :: String -> Recipe ()
+

Problem: How to pass the output of first recipe into the second recipe?

+
















+

QUIZ: Using Yolks to Make Batter

+

Suppose you have two recipes

+
crack     :: Recipe Yolk
+eggBatter :: Yolk -> Recipe Batter
+

and we want to get

+
mkBatter :: Recipe Batter
+mkBatter = crack `combineWithResult` eggBatter
+

What must the type of combineWithResult be?

+
{- A -} Yolk -> Batter -> Batter
+{- B -} Recipe Yolk -> (Yolk  -> Recipe Batter) -> Recipe Batter
+{- C -} Recipe a    -> (a     -> Recipe a     ) -> Recipe a
+{- D -} Recipe a    -> (a     -> Recipe b     ) -> Recipe b
+{- E -} Recipe Yolk -> (Yolk  -> Recipe Batter) -> Recipe ()
+
















+

Looks Familiar

+

Wait a bit, the signature looks familiar!

+
combineWithResult :: Recipe a -> (a -> Recipe b) -> Recipe b
+

Remember this

+
(>>=)             :: Result a -> (a -> Result b) -> Result b
+












+

Recipe is an instance of Monad

+

In fact, in the standard library

+
instance Monad Recipe where
+  (>>=) = {-... combineWithResult... -}
+

So we can put this together with putStrLn to get:

+
main :: Recipe ()
+main = getLine >>= \name -> putStrLn ("Hello, " ++ name ++ "!")
+

or, using do notation the above becomes

+
main :: Recipe ()
+main = do name <- getLine
+          putStrLn ("Hello, " ++ name ++ "!")
+












+

EXERCISE

+
    +
  1. Compile and run to make sure its ok!

  2. +
  3. Modify the above to repeatedly ask for names.

  4. +
  5. Extend the above to print a “prompt” that tells you how many iterations have occurred.

  6. +
+



















+

Monads are Amazing

+

Monads have had a revolutionary influence in PL, well beyond Haskell, some recent examples

+
    +
  • Error handling in go e.g. 1 and 2

  • +
  • Asynchrony in JavaScript e.g. 1 and 2

  • +
  • Big data pipelines e.g. LinQ and TensorFlow

  • +
+








+

A Silly App to End CSE 130

+

Lets write an app called moo inspired by cowsay

+

A Command Line App

+
+
moo
+
+

moo works with pipes

+
+
Thanks, and good luck for the final!
+
+ +
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/lectures/info_ocaml.html b/docs/lectures/info_ocaml.html new file mode 100644 index 0000000..50f6dcc --- /dev/null +++ b/docs/lectures/info_ocaml.html @@ -0,0 +1,337 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

How to run Ocaml in the Lab machines

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

Entering the Ocaml Shell

+

You need to type prep cs130s before you can run Ocaml.

+

Here is a sample session.

+
$ prep cs130s
+
+$ ocaml
+        OCaml version 4.02.3
+
+# 2 + 3;;
+- : int = 5
+
+# Printf.printf "Hello, %s \n" "world" ;;
+Hello, world
+- : unit = ()
+

If you run Ocaml on your own machine, then do

+
$ rlwrap ocaml
+

The rlwrap is optional but rather nice as it lets you hit the up/down cursors to go to your old commands (to avoid typing them out again.) We have used alias so you don’t have to type it out on ieng6.

+

Loading Files

+

Enter

+
#use "foo.ml";;
+

to load the file foo.ml. This has the same effect as typing out the file at the prompt, but its usually more convenient to type and edit the file with Vim/Emacs/Atom and load it in like this. (A more convenient option is the OCaml-mode for Emacs.)

+

Press Ctrl-D to exit OCaml.

+

Directly Running Ocaml from Bash

+

You can directly run the file, i.e. send the file foo.ml into OCaml and get the output piped to stdout by typing:

+
$ ocaml foo.ml
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/lectures/info_prolog.html b/docs/lectures/info_prolog.html new file mode 100644 index 0000000..02e83e0 --- /dev/null +++ b/docs/lectures/info_prolog.html @@ -0,0 +1,347 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

How to run Prolog in the Lab machines

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

Entering the Prolog Shell

+

Here is a sample session. We use $ for the Unix shell prompt, and ?- for the Prolog shell prompt.

+
$ rlwrap swipl
+
+130f@ieng6-202]:~:501$ swipl
+Welcome to SWI-Prolog (Multi-threaded, 32 bits, Version 5.10.5)
+Copyright (c) 1990-2011 University of Amsterdam, VU Amsterdam
+SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software,
+and you are welcome to redistribute it under certain conditions.
+Please visit http://www.swi-prolog.org for details.
+
+For help, use ?- help(Topic). or ?- apropos(Word).
+
+?- X = cat.
+X = cat.
+
+?- X = Y.
+X = Y.
+
+?- halt.
+

To load a program lec-prolog.pl from disk use the consult command like so:

+
?- consult('lec-prolog.pl').
+% foo.pl compiled 0.00 sec, 10,640 bytes
+true.
+
+?- parent(albert, z).
+false.
+
+?- parent(albert, Z).
+Z = felix ;
+Z = dana.
+
+?- parent(Z, dana).
+Z = albert ;
+false.
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/lectures/partial-solutions.html b/docs/lectures/partial-solutions.html new file mode 100644 index 0000000..68118cf --- /dev/null +++ b/docs/lectures/partial-solutions.html @@ -0,0 +1,428 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

Partial Solution Key For Sample Exams

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

Final Fall 06

+

6 (a)

+
def elementAndRest[A](xs: List[A]): Iterator[(A, List[A])] = {
+  for (i <- (0 until xs.length).iterator)
+    yield (xs(i), xs.slice(0, i) ++ xs.slice(i+1, xs.length))
+}
+

6 (b)

+
def permutations[A](xs: List[A]): Iterator[List[A]] =
+  xs match {
+    case Nil     =>
+      Iterator(List())
+    case x::rest =>
+      for ( ys <- permutations(rest)
+          ; i  <- 0 until xs.length)
+      yield ys.slice(0, i) ++ List(x) ++ ys.slice(i, ys.length)
+  }
+

Fall 07

+

6 (d)

+
object tick {
+  private var ctr = 0
+  def apply() = {
+    ctr += 1
+    ctr
+  }
+}
+

7 (a)

+
def valid(es:List[(Int, Int)], c: List[Int]): Boolean =
+  es.forall(e => c(e._1) != c(e._2))
+

7 (b)

+
def colorings(n: Int, k: Int): List[List[Int]] = {
+  if (n <= 0) List(List())
+  else { for ( cs <- colorings(n-1, k)
+             ;  c <- 0 until k)
+         yield (c::cs) }
+}
+

7 (c)

+
def initColoring(n: Int) =
+  (0 until n).toList.map(x => 0)
+
+def lastColoring(xs: List[Int], k: Int) =
+  xs.forall(_ == k-1)
+
+def nextColoring(xs: List[Int], k: Int) = {
+  var res : List[Int] = List()
+  var carry = 1
+  for (i <- (xs.length - 1) to 0 by -1) {
+    val sum = carry + xs(i)
+    val dig = sum % k
+    carry   = sum / k
+    res     = dig :: res
+  }
+  res
+}
+

Midterm Spring 12

+

1 (a)

+
val (<.>) : ('b -> 'c) -> ('a -> 'b) -> ('a -> 'c)
+

1 (b)

+
10
+

1 (c)

+
let giftList =
+  let rec helper acc xs = match xs with
+    | []     -> acc^" and thats what I want for Christmas!"
+    | x::xs' -> helper (acc ^ " and ") xs'
+  in helper ""
+

2 (a)

+
val getEven : int list -> int option
+

2 (b)

+
let rec find_first f xs = match xs with
+  | []     -> None
+  | x::xs' -> if f x then Some x else find_first f xs'
+

2 (c)

+
val tree_to_string: string tree -> string
+

2 (d)

+
let rec post_fold f b t = match t with
+  | Leaf           -> b
+  | Node (x, l, r) -> f x (post_fold f b l) (post_fold f b r)
+

2 (e)

+
let rec in_fold f b t = match t with
+  | Leaf           -> b
+  | Node (x, l, r) -> let bl = in_fold f b l  in
+                      let bx = f bl x         in
+                      let br = in_fold f bx r in
+                      br
+

3 (a)

+
Left 3
+

3 (b)

+
Right "monkey"
+

3 (c)

+
let rec assoc key kvs = match kvs with
+  | (k, v)::rest -> if key = k then Right v else assoc key rest
+  | []           -> Left key
+

3 (d)

+
let map f e = match e with
+  | Left l -> Left l
+  | Right r -> Right (f r)
+

3 (e)

+
let map2 f e1 e2 = match (e1, e2) with
+  | Right r1, Right r2 -> Right (f r1 r2)
+  | Left l, _  -> Left l
+  | _, Left l  -> Left l
+

4 (a)

+
let lookup x env = match assoc x env with
+  | Left z  -> Left (UnboundVariable z)
+  | Right i -> Right i
+

4 (b)

+
let safeDiv n m =
+  if m != 0 then Right (n / m) else Left DivideByZero
+

4 (c)

+
let rec eval env e = match e with
+  | Const i            -> Right i
+  | Var v              -> lookup v env
+  | Bin (e1, Plus, e2) -> map2 (+) (eval env e1) (eval env e2)
+  | Bin (e1, Div, e2)  -> (match (eval env e1) (eval env e2) with
+                           | _, Right 0 -> Left DivideByZero
+                           | Right i1, Right i2 -> Right (i1/ i2)
+                           | Left l, _ | _, Left l -> Left l)
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/lectures/piazza.html b/docs/lectures/piazza.html new file mode 100644 index 0000000..5f133b7 --- /dev/null +++ b/docs/lectures/piazza.html @@ -0,0 +1,322 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

Piazza Discussions

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

Lambda Calculus 4/7

+
    +
  • https://piazza.com/class/j0va76qxb1s6gw?cid=26
  • +
  • https://piazza.com/class/j0va76qxb1s6gw?cid=19
  • +
+

4/10

+
    +
  • https://piazza.com/class/j0va76qxb1s6gw?cid=56
  • +
  • https://piazza.com/class/j0va76qxb1s6gw?cid=79
  • +
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/lectures/prolog.html b/docs/lectures/prolog.html new file mode 100644 index 0000000..ede553f --- /dev/null +++ b/docs/lectures/prolog.html @@ -0,0 +1,1063 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

Logic Programming

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

Logic Programming

+

(adapted from lecture notes by Henri Casanova and Todd Millstein)

+

Introduction

+

We now turn to a brand new paradigm, called “Logic Programming Languages” – our vehicle for studying this paradigm is Prolog, whose roots are in logic and on automated theorem proving. Prolog was developed in the 1970s for AI applications. Some such applications have a knowledge base (or database) of facts, from which you’d like to ask queries and deduce other facts. For example, given facts in the knowledge base like “Carnitas is Mexican” and “Mexican food is delicious”, then we can deduce “Carnitas is delicious”.

+

Prolog probably looks nothing like any language you’ve seen before. Fundamental difference between Prolog and most other languages:

+
  You don't run a Prolog Program
+

Instead, you ask questions and the system attempts to answer them using the rules and facts that it has been given.

+
  Logic programs are "declarative": the specification of the
+  desired results are written, rather than how to obtain them.
+

This approach is very good at expressing problems that involve searching a large space of possibilities. For example, given a list of cities and distances between them, find me the shortest route that passes through each one once (the travelling salesman problem).

+

The philosophy of this approach is that it is often hard to specify a search algorithm – and in such cases, it is easier to specify the characteristics of the solution. To do so, you specify facts and rules for deducing new facts from old facts, and then a query. So you just state what is true and then ask what (else) is true. The language implementation figures out how to actually compute appropriate solutions. Use the travelling salesman problem as an illustration: I say the constraints, not how to search the space of possible solutions. Of course, as we will see, this is a simplification, and often for reasons of efficiency, one has to impose constraints on the search.

+

The original and principal applications for Prolog are in various AI settings such as (expert databases). Examples include using prolog-based databases to determine when credit card fraud has occurred (prolog is used to specify rules that indicate when a fraud occurs), and there are projects afoot to use ideas from prolog to determine suspicious people from phone/communication patterns. Note that currently AI researchers devise statistical techniques to complement (if not replace) logical approaches for such tasks. Another big application is as a database query language.
+E.g., my facts are things like the daily stock prices of various stocks over the last year. Queries can be things like: find me all pairs of stocks that had the same price on the same day at least 50 times this year.
+Standard query languages like SQL are inspired by and are really subsets of Prolog. Of course, the reason we’re studying it is that its a radically different way of thinking about computation: “programming as proving”. You’ll be surprised at how many places this paradigm fits beautifully, or leads to very elegant and readable systems.

+

Terms

+

Based on propositional logic. The entire program comprises three kinds of elements: facts, rules, and queries. The basic unit of each of these are terms.

+

Terms are Prolog’s way of encoding data. They are very similar to the values of datatypes created in ML. There are three kinds of terms: constants, variables and compound terms.

+
    +
  1. Constants: The simplest kind of terms are constants.
  2. +
+
    +
  • integers and reals: are constants.

  • +
  • atoms: are identifiers starting with a lowercase letter.

    +

    For example:

    +

    alice, bob, charlie

    +

    are all atoms.

    +

    Atoms are NOT variables – the way to think of them is as TAGS, or special constants, or elements of a giant enum datatype. They are similar to the tags used in ML datatypes:

  • +
+
    type day = Alice | Bob | Charlie ...
+
Only in ML, the tags start with a capital letter.
+
+Atoms are *uninterpreted constants*:  nothing is known about each tag
+except that it is equal to itself.
+
+Intuitively, Prolog knows that:
+
+`alice = alice`
+
+as the tags are the same. However, to it,
+
+`alice = bob`
+
+*never* makes sense, as it has a strange notion of equality, that we
+will see shortly.
+
+There are some built-in atoms such as `[]` that signifies the empty
+list, `.` which is used for list concatenation and so on, that we will
+see shortly.
+
    +
  1. Variables: Any identifier beginning with an upper case letter or an underscore is a variable.

    +

    For example,

    +

    X, Y, Head, Tail, Alfred

    +

    are all variables.

    +

    The variable _ is like a wildcard" variable, whose meaning is similar to the _ in ML, but more on that when we see what a variable means.

    +

    As we shall see:

    +

    x = a is nonsense (as x and a are two different constants/tags). – X = a has some sense (but isnt at all what one might think…).

    +

    WARNING: Upercase/Lowercase is a common source of error! Also, variables are NOT declared before use (so be careful!).

  2. +
  3. Compound Terms: These are terms of the form: atom(term,term,term,…) where each “term” is either an atom or a variable or a compound term. Examples include:

  4. +
+
      x(y,z)
+      parent(alice,bob).   %here alice,bob are atoms
+      parent(alice,Child). %here alice is an atom, Child is a variable
+

In other words, (compound) terms are generated by the following grammar:

+
	atom := [a-z][A-z,a-z,0-9]* | [0-9]* | ...
+
+	variable := [A-Z][a-z,A-Z,0-9]* | _
+
+	term := atom | variable | atom(term,term,term,...)
+

While you may be tempted to think of compound terms like:

+

parent(alice, bob)

+

as function calls, they are NOT! Instead, you should think about this in the same way as we thought of ML the recursive, one-of types in ML.

+
   type term =   alice | bob | charlie | ... (* other atoms *)
+             | Var of string
+	           | Parent of term * term
+

Thus, parent(alice, bob) in Prolog is “equivalent to” the ML value:

+

Parent(alice, bob)

+

which is just a tuple alice, bob with a tag Parent on it, or equivalently represented as a tree:

+
          Parent
+			     /  \
+			    /    \
+			  alice  bob
+

and parent(alice,Charlie) in Prolog is “equivalent to” the ML value: Parent(alice,Var (“Charlie”)), or represented as a tree:

+
			    Parent
+			     /  \
+			    /    \
+			  alice  Var
+				  |
+				  |
+				Charlie
+

Consider a term: factorial(5)

+

It is NOT a function (despite what it looks like).

+
    +
  • there is no associated function implementation
  • +
+

Prolog has no idea of the meaning you intend for this term – to it, this is just a box containing 5 with a label factorial.

+

Another way to view it is as a tree:

+
			factorial
+			    |
+			    |
+			    5
+

Thus, the only thing Prolog knows is that:

+

factorial(5)=factorial(5)

+

i.e. the two terms are the same. In particular, to Prolog, factorial(5)=120 is NOT true.

+

Thus Prolog compound terms are really just structured data – like values of a datatype in ML. Can also think of atoms that begin a compound term as uninterpreted functions: e.g., factorial is a function about which NOTHING is known, except that the result of applying this function to the atom x, is the term factorial(x).

+

These atoms are called function symbols.

+

Facts

+

A fact is just a term, typically without any variables. You specify a fact by writing a term followed by a ‘.’. For example, here are a few facts that one might have in the system.

+
% List of parent relationships
+parent(kim,holly).  
+parent(margaret,kim).  
+parent(herbert,margaret).
+parent(john,kim).
+parent(felix,john).  
+parent(albert,felix).
+

Note that kim, holly, margaret, herbert, john, kim, felix, albert are all atoms.

+

The Prolog interpreter maintains a collection of facts like the above – think of it as the underlying data in the database over which queries will be asked.
+You can define your own facts and add them to the database. The function symbols beginning a fact are called predicates: intuitively, they represent functions that evaluate to a boolean.

+

Thus, (the atom) parent is a predicate that, intuitively, takes two arguements and returns a boolean – we say that parent is a predicate of arity 2.

+

The key thing to note, is that predicates have no intrinsic meaning. However, they are generally designed and named so that the programmer can easily “interpret” them.

+

For example, as a programmer, I will decide that

+
parent(X,Y)
+

means that X is a parent of Y. In other words, I will specify the fact:

+
parent(a,b).
+

only if the person corresponding to atom a is a parent of the person corresponding to atom b. Thus, the predicate is interpreted as a logical relation between X and Y.

+

Prolog uses these facts to answer queries, as well as to infer new facts.

+

Lets see how it does the first.

+

Running Prolog : Queries

+

HEREHEREHEREHERE

+

The standard interface to Prolog is in an interactive shell. To run it, first, lets put a bunch of facts into a file, and then load the file into the shell. Suppose the list of facts are stored in a file called “facts.pl”. First, we load prolog, and get the shell prompt:

+

Prolog prompt: “?-”

+

At this prompt, enter something like:

+

?- consult(‘facts.prolog’).

+

You can manually add the facts one by one by typing at the prompt:

+

?- assert(parent(margaret,kim)).

+

(or whatever fact you want to insert).

+

Once this is done, the facts have been registered into the shell, we can query Prolog as follows:

+
1. Prompts you to type a query
+2. You type a query
+3. Prolog tries to prove your query
+4. Prints out the result (or 'failure')
+5. Repeat
+

The simplest query is a term followed by a ‘.’ (looks like a fact, but is just typed at the prompt). For example, suppose you type the following query:

+

?- parent(margaret, john).

+

The meaning of a query is “is this fact in your database or can it be inferred from your database”. In other words, we are asking Prolog if it can PROVE the fact. Prolog replies:

+

No

+

as this is not one of the facts (we have not yet given it any rules to infer new facts). Instead, if we were to ask:

+

?- parent(margaret,kim).

+

Prolog replies:

+

Yes

+

As this is in the database of facts we fed in. (Tip: If you forget the period then you can type it on the next line) Not bad, but not especially exciting – we gave it a bunch of facts, and basically each query is effectively asking if the query term was one of the facts we supplied.

+

Things get more fun, when we toss variables into the queries.

+
  ?- parent(margaret,X).
+

This is where we Prolog departs radically from other paradigms. The meaning of this query is:

+

“What value(s) can you plug in for X such that the fact becomes provable for that value ?” Prolog replies:

+
  X = kim 	[press enter if you're satisfied]
+Yes
+

This means that it can plug in “kim” for X, and thus, it can deduce the fact: parent(margaret,kim). Instead, you can enter the query:

+
  ?- parent(X,kim).
+

This asks prolog, for what values of X does the fact parent( _ , kim) hold. In other words, this innocent query is asking prolog – who are the (known) parents of kim ? It replies:

+
X = margaret   ;	[press ';' if you want another answer]
+    X = john  ;
+No
+

Thus, it returns, one-by-one, all the “solutions” for X that make the fact parent(X,kim) provable. We can make both the parameters variables:

+
  ?- parent(X,Y).  
+

This asks prolog – what are the pairs X,Y such that X is (provably) the parent of Y ? It responds:

+
X=kim  Y=holly ;
+X=margaret  Y=kim;
+...
+

Suppose you want to know if there are any strange circularities in your fact database – for example, does there exist any person who is their own parent ? The following query does the trick:

+
  ?- parent(X,X).  
+  	No
+

——————————- Unification ———————————–

+

In most other languages, a function designed to look up things like this would be less flexible – it would require tedious parentOf() method or a childOf() method, loops, etc. With Prolog – the queries are very flexible, and work like magic. Whats going on ?

+

Turns out that Prolog’s computational heart is a fancy pattern matching technique (also, btw, at the heart of ML’s type system) borrowed from logicians, called “Unification”. Regrettably, we won’t have time to go into the details of unification – and so, lets content ourselves with a cartoon version.

+
Intuitively, two terms can be unified   if there a
+way of assigning values to their variables so that
+the terms become identical.
+

This is really what " = " means in prolog – when you ask it:

+
?-  t1 = t2.
+

For any two terms t1 and t2, you are asking it whether the terms t1 and t2 can be unified. So, if you ask:

+

?- foo(bar) = foo(bar). Yes

+

Because there are no variables and the terms are the same. If instead you ask:

+

?- foo(X) = foo(bar).

+

It replies: X = bar Yes

+

Meaning that foo(X) can be unified with foo(bar) by assigning the variable X to the term “bar”. Note that we can ask this from Prolog without “declaring” any of the above atoms. This is because everything is symbolic – everything is a term which is an arbitrary notation that can encode whatever concept. One can type the above right after starting the interpreter.

+

The more interesting case is when there are several variable in the terms:

+

?- p(X,dog) = p(cat,Y). X = cat Y = dog Yes

+

meaning that one can unify the terms p(X,dog) and p(cat,Y) by assigning appropriate values to X and Y. However, if we were to ask:

+

?- p(cat) = p(dog). No

+

As the terms are different, and so, if we ask: ?- q(X,dog,X) = p(cat,Y,Y). No

+

is the answer as to unify, X must be “cat” and Y must be “dog”, but this ensures that the last parameter of the term can never be the same!

+

Similarly, the unification happens “deep” into the terms:

+

?- a(W,foo(W,Y),Y) = a(2,foo(X,3),Z). W = 2 X = 2 Y = 3 Z = 3 Yes

+

Intuitively, it first matches up the first position, and so W gets 2, next, it tries to match up the second position – i.e.

+
foo(W,Y) with foo(X,3).
+

now, W is already 2, so X also gets 2, and Y gets 3. Finally, it tries to match up the last position, and Y is 3 and so Z gets 3.

+

Instead, the query:

+

?- a(W,foo(W,Y),Y) = a(2,foo(X,3),X). No

+

Thus, by using W,X,Y in two places, we are forcing it to find a solution where those two places get exactly the same value. As a result, the constraints ensure that all the variables must get the same value. However, W and Y must get 2 and 3 and so there is no solution.

+

Thus, this innocent “pattern matching” operation actually does a lot under its hood, and it turns out to be a surprisingly powerful and flexible way to encode all kinds of computation! All the queries that we asked before, were answered via unification.

+

When we ask:

+
?- parent(margaret, john).  
+

prolog checks if the term “parent(margaret,john)” can be unified with any of the known facts (that are also terms). If so, it says Yes, but as it cannot, it replies No.

+

When we ask:

+
?- parent(margaret,kim).
+

it can unify the query term with a known fact term, and so it replies Yes.

+

When we ask:

+
?- parent(margaret,X).
+

it tries to find all the known facts with which it can unify the query term – there is only one, and so it “answers the query” by returning the substitution required for unification: X = kim

+

Similarly, when we ask:

+
  ?- parent(X,kim).
+

It attempts to find all the known terms with which this query can be unified – this time, there are several terms, and the different valid unifying substitutions (called unifiers) yield the different parents of kim: X = margaret ; X = john ; No

+

Finally, to answer the query:

+
?- parent(X,Y).
+

It attempts to unify the query term with all known facts – and the list of resulting unifiers is exactly the set of known parent child pairs.

+

——————————- Conjunction ——————————

+

Often, its more useful to ask questions about several terms. For example, to determine if margaret is holly’s grandparent, we would like to find if there is some person who is both the child of margaret AND the parent of holly. To do so, we can issue a conjunctive query which is a list of terms separated by commas as follows:

+
  ?- parent(margaret, X), parent(X, holly).
+

To answer this query, Prolog attempts to find an X such that parent(margaret,X) unifies with the set of known facts, AND, parent(X,holly) unifies with the set of known facts. Upon finding a unifier that works, it replies:

+
X = kim
+Yes
+

Thus, as kim is the intermediate parent, we can conclude that margaret is indeed a grandparent of holly. Finally, consider the following query:

+
  ?- parent(X,Y), parent(Y,Z), parent(Z,kim).
+

It asks if there are X,Y,Z such that X is Y’s parent, Y is Z’s parent and Z is kim’s parent. In other words, the query determines if kim has any “great-grandparent”. Upon finding appropriate unifiers, prolog replies:

+
X = john
+Y = felix
+Z = albert
+Yes
+

——————————- Rules ————————————

+

The above is quite nifty – it allows us to quickly mine the database to find interesting relationships. However, it gets somewhat cumbersome as we have to devise a complex conjunctive query ever time. Instead, it would be nice if we could define complex queries out of simpler queries.

+

Rules serve exactly that purpose. They allow us to specify complex queries (i.e. predicates) using simpler ones. In general, the format of a rule is:

+
 head :- condition1, condition2, condition3....
+

Intuitively, it means, that the “head” query is true if condition1, condition2, condition3,… are all true. In other words, it tells prolog, to prove the head query, prove the conditions 1,2,3.

+

For example, suppose we’d like to define a grandparent relationship (predicate). We do so as:

+
grandparent(GP,GC) :- parent(GP,P), parent (P,GC).
+

Intuitively this states:

+

“GP is a grandparent of GC if GP is a parent of P AND P is a parent of GC”.

+

With this definition, we can now issue the following query:

+
  ?- grandparent(X,kim).
+

and prolog responds with:

+
X=herbert
+Yes
+

as it can find that parent(herbert,margaret) and parent(margaret,kim), therefore, applying the rule, grandparent(herbert,kim), to which fact the query term gets unified. We can use this predicate to write more predicates:

+
greatgrandparent(GGP,GGC) :- parent(GGP,GP) , grandparent (GP, GGC).
+

We can now issue the query:

+
?- greatgrandparent(X,holly).
+   X=herbert
+   Yes
+

Program = Facts + Rules:

+

Facts and Rules are the two kinds of “Clauses” (intuitively, a fact is just a rule without any conditions). Thus, a prolog program is a set of clauses – partitioned into a database of facts and a set of rules for inferring new facts.

+

Scope:

+

Notice that the same variable P appears twice in the grandparent rule. Indeed one may be tempted to reuse P across several rules. In Prolog, the scope of a variable is the clause (rule) that contains it. Thus, there is no connection whatsoever between variables across clauses.

+

For example, consider the two clauses:

+
   foo(P) :- bar(P).        % There is no connection between P in
+   stuff(P) :- thing(P).    % the 2 clauses.
+

In other words, there are no global variables, all variables are local to the individual clauses.

+

———— Multiple Clauses = Disjunction and Recursion —————

+

Suppose we want to define a predicate that is true for all those persons that have some family – that is, those persons who have either a parent OR a child. We can do so as follows:

+
has_family(X) :- parent(X,_).
+has_family(X) :- parent(_,X).
+

If we have multiple rules for the same predicate, effectively we are specifying a disjunction. The first rule says:

+

"X has a family if there is some _ such that X is the parent of _"

+

The second rule says:

+

“X has a family if there is some _ such that _ is the parent of X”

+

If either of these clauses fire then, has_family(X) becomes true. Like in ML, the symbol "_" represents a “wildcard” or dont-care variable that we will use only in one place and so we not bother to name it.

+

Thus, ?- has_family(holly). Yes

+

as the second rule fires for holly. While,

+

?- has_family(mugatu). No

+

as neither rule fires for mugatu.

+

For those of you who are economical with the keystrokes, there is another way to specify disjunctive rules – via a semicolon:

+

has_family(X) :- parent(X,) ; parent(,X).

+

Suppose you want to specify an predicate ancestor(X,Y) which is true if X is an ancestor of Y, i.e. if by following the parent relationship from Y one eventually reaches X. Intuitively, X is an ancestor of Y either if:

+
1. X is the parent of Y, or,
+2. Z is the parent of Y, and X is an ancestor of Z.
+

Thus, we can specify this predicate recursively as follows:

+
ancestor(X,Y) :- parent(X,Y).                %[Base case]
+ancestor(X,Y) :- parent(Z,Y),ancestor(X,Z)   %[Recursive case]
+

This works quite niftily:

+
?- ancestor(kim,X).
+   X = holly ;
+   No
+

i.e. holly is the only “descendant” of kim, and:

+
?- ancestor(X,kim).
+   X = margaret ;
+   X = john ;
+   X = herbert ;
+   X = felix ;
+   X = albert ;
+   No
+

i.e. kim has a long ancestry.

+

—————————– Backtracking Search —————————

+

At this point, its worth looking into how exactly prolog pulls off this trick of answering queries in this manner, as it has its limits, which one needs to know to phrase the queries appropriately. Turns out, there’s no real magic – just brute force “proof” search.

+

We can view each clause as a “proof rule”:

+
  goal :- subgoal_1, subgoal_2,...
+

Thus, the rules for ancestor are as follows:

+

ancestor(X,Y) :- parent(X,Y). %rule 1 ancestor(X,Y) :- parent(Z,Y),ancestor(X,Z). %rule 2

+

To prolog, these rules mean the following – to prove ancestor(X,Y), try to:

+
    +
  1. prove the subgoal parent(X,Y), or, failing that,
  2. +
  3. prove the subgoal parent(X,Z), and then the subgoal ancestor(X,Z).
  4. +
+

Thus, suppose we ask it the query:

+

?- ancestor(felix,holly).

+

To prove this query, it undertakes the following backtracking search:

+
	ancestor(felix,holly)?
+	  /		                \
+

parent(felix,holly) parent(Z,holly) NO ancestor(felix,Z) | | Z = kim (by fact) | ancestor(felix,kim) /
+parent(felix,kim) parent(Z’,kim) NO ancestor(felix,Z’) ———-| | | Z’=john Z’=margaret | | | ancestor(felix,john) ancestor(felix,margaret) | /   parent(felix,john) parent(felix,margaret) parent(Z’‘,margaret) YES NO ancestor(felix,Z’‘) | Z’’ = herbert | | ancestor(felix, herbert) / | parent(felix,herbert) parent(Z’’’,herbert) NO NO

+

Thus, it first tries the base rule i.e. to prove the subgoal parent(felix,holly). As it cannot unify this query with any known fact, it fails (NO), and so it backtracks and tries the other recursive rule. The only Z such that parent(Z,holly) unifies with a known fact is when Z=kim, thus, it tries to prove the second subgoal ancestor(felix,kim). To do so, again it first applies the base rule, which fails, and so it backtracks and applies the recursive rule.

+

This time, there are two different Z (written Z’ in the figure to distinguish from the upper part of the tree), such that parent(Z,kim) – namely Z=margaret and Z=john. It picks margaret first (as that is the first unification that succeeds, and tries to prove the the second subgoal ancestor(felix,margaret). As we can see, from the subtree, this search fails, (as margaret’s sole parent is herbert who has no parent).

+

Thus, prolog backtracks and tries the second Z=john, and tries to prove the second subgoal, ancestor(felix,john). This time, the base rule works as parent(felix,john) is a known fact, and thus the proof search succeeds and prolog returns:

+

?- ancestor(felix,holly). Yes

+

This same process is repeated for any query. When there is a variable in the query, eg.

+

?- ancestor(X,kim).

+

Prolog attempts the proof search and returns all the unifiers for X for which the proof succeeds. Thus, prolog is literally programming by proving.

+

Hint: Trace mode in prolog shows the tree:

+
?- trace.
+

The subsequent query is traced: use the on-line help on the ACS Prolog interpreter

+
?- help(trace).
+

Order Matters:

+

The rub is that the order of the clauses and terms influences greatly the order in which the unification and backtracking happens. This is because, to prove a particular goal query, the different clauses are selected in order, and further, within each clause, the subgoals are selected from left-to-right, which affects how the unification happens.

+

In the above example, if we had entered the branch Z’=john rather than than the branch Z’=margaret, then we would have proven the query faster. Similarly, if we had swapped the order of the conjunctions (subgoals) in the recursive clause, we would have a rather different tree (try as an exercise). Thus, order matters for performance. Hint: Try simple things first!

+

More importantly, there are cases where the program may not even work (may not terminate), depending on the order:

+
ancestor(X,Y) :- ancestor(X,Z), parent(Z,Y).
+ancestor(X,Y) :- parent(X,Y).
+

Now lets try the same query:

+
?- ancestor(felix,holly).
+ ERROR: Out of local stack
+

Why ? Well, if you try to build the search tree, you’ll see it goes forever:

+
	ancestor(felix,holly)?
+	  |
+		|
+		|
+	ancestor(felix,Z)  %prove first subgoal,
+		|          %then parent(Z,holly)
+		|
+		|
+	ancestor(felix,Z') %prove first subgoal,
+		|	   %then parent(Z',Z)
+		|
+		|
+	ancestor(felix,Z'')
+		.
+		.
+		.
+

So, to avoid this, we must place the parent subgoal first (in the recursive rule). If this is done, the unification with the base facts (parent), fix the possible unifiers for Z, thereby guaranteeing termination.

+

HEREHEREHEREHERE

+

Lets see another example. Suppose we want to define a sibling predicate, where sibling(X,Y) holds if X and Y have the same parent. How about:

+
sibling(X,Y) :- parent(P,X), parent(P,Y).
+

Almost:

+
? sibling(kim,kim).
+Yes
+

Ah, we have to ensure that X and Y are not the same. Ok, how about:

+
sibling(X,Y) :- not(X=Y), parent(P,X), parent(P,Y).
+

Surely this works ? Nope. The reason is prolog’s semantics of equality (i.e. unification). This clause is read by prolog as:

+

first, find a X,Y such that X cannot be unified with Y, then, find a P such that parent(P,X), and parent(P,Y).

+

Now the catch is that to process the first subgoal, prolog finds it can always unify (two unconstrained variables) X and Y, by simply assigning X to Y! ?- X=Y. X=Y Yes

+
?- not(X=Y).
+No
+

Thus, the very first subgoal always fails, thereby ensuring that:

+
?- sibling(X,Y).
+No
+

Thus, to get the rule right, we must make sure that the goal that ensures that X and Y are not the same, is fired AFTER X and Y have been unified with appropriate atoms. We can do so by simply placing the subgoal at the end. sibling(X,Y) :- parent(P,X), parent(P,Y), not(X=Y).

+
?- sibling(X,Y).
+X = john
+Y = maya ;
+
+X = felix
+Y = dana ;
+
+X = dana
+Y = felix ;
+
+X = maya
+Y = john ;
+No
+

This shows a major weakness: You can’s just rely on the logical meanings and you sort of need to know how things work.

+

Oh well, nothing’s perfect.

+

We’ll see that many, many things break down the pure philosophy that says: “Just write what you need logically”

+

—————————- Numeric Computation ————————–

+

Although Prolog is mostly symbolic, there is a need for numeric computation.

+
    +
  • ‘=’ is the unification operator ?- X = 2+3. X = 2+3 Yes
  • +
  • ‘is’ evaluates arithmetic expressions before doing unification ?- X is 2+3. X = 5 Yes
  • +
+

When prolog tries to solve an “is” goal it evaluates the second argument and then unifies, as opposed to “=” which just does the unification.

+
?- Y is X+2, X=1.
+

ERROR: Args are not sufficiently instantiated

+
?- X=1, Y is X+2.
+      X=1
+      Y=3
+      Yes
+

Again, order of evaluation matters!

+

Functions are Predicates:

+

Lets try to write a factorial function in Prolog. We need to somehow encode functions as predicates. Here’s the deal: Whenever you have a function f(x), you can write a predicate

+
pred_f(X,Y)
+

that captures the behavior of f by being true for all those pairs X,Y where Y is f(X).

+

Thus, we can write a predicate capturing the input/output relationship of the factorial function – i.e. a predicate factorial(X,Y), that is true for those pairs X,Y where Y is the factorial of X.

+
factorial(0,1). % base case
+factorial(X,N):- X1 is X-1, factorial(X1,N1), N is X1*N1.
+

We “call” the function with a query.

+
?- factorial(0,X).
+X = 1
+Yes
+
+?- factorial(5,X).
+X = 120
+

—————————- Data Structures: Lists ——————————–

+

Let us now see how we can encode lists in Prolog. Again, its useful to recall how lists were encoded as a datatype in ML. – There is a “base atom”: [] denoting the empty list – There is a “cons”tructor: | (different syntax for this).

+

Thus, ML’s list Cons(1,Cons(2,Cons(3,Nil))) is equivalent to the prolog term:

+
[1|[2|[3|[]]]]
+

where (1) | is Cons, and (2) [] is Nil.

+

Also, as they are heavily used, Prolog lets you write the above term as: [1,2,3].

+
?- [1,2,3] = [1|[2|[3|[]]]].
+Yes
+

To “deconstruct” a list into head and tail, we use pattern-matching (very much like in ML). So:

+

[X|Y] unifies with any non-empty list (like h::t), X unified to the first element (head) Y unified to the rest of the list (tail).

+
?- [X|Y] = [1,2,3,4,5].
+X = 1
+Y = [2,3,4,5]
+Yes
+
+?- [X|Y] = [1].
+X = 1
+Yes
+
+?- [X|Y] = [].
+No
+

[1|Y] unifies with any list starting with 1 (like 1::t), Y unified to the rest of the list.

+

However, prolog also lets you write:

+

[1,2|X] which unifies with any list that starts with 1 and then 2. ?- [1,2|X] = [1,2,3,4,5]. X = [3,4,5] Yes

+
?- [1,2|X] = [1,2]
+   X = []
+   Yes
+
+?- [1,2|X] = [1,3]
+   No
+
+One can place variables wherever in the term, so:
+    ?- [X,Y|Z] = [1,2,3].
+       X = 1
+       Y = 2
+       Z = [3]
+

Ok – how do we do interesting things with lists. For example, how might we “append” two lists ? Well, there is no “concatenation” or sticking together. In prolog you write a predicate:

+
append(X,Y,Z)
+

which is true if Z is the result of appending the lists X and Y. Turns out such a predicate is built-in, so lets see what it does.

+
  ?- append([1,2],[3,4],Z).
+    Z=[1,2,3,4]
+Yes
+

It simply “solves” for the right Z that happens to be the result of appending [1,2] and [3,4], but wait, predicates can do more:

+
  ?- append(X,[3,4],[1,2,3,4]).
+    X=[1,2]
+

whoa! backwards computation – what X is such that when appended to [3,4] you get [1,2,3,4] ? And now, the full power of multiple solutions:

+
  ?- append(X,Y,[1,2,3]).
+    X = []
+    Y = [1,2,3] ;
+
+    X = [1]
+    Y = [2,3] ;
+
+    X = [1,2]
+    Y = [3] ;
+
+    X = [1,2,3]
+    Y = []
+    Yes
+

Try doing that in another language.

+

There are several such predicates for reverse, sort, append, built-in, but lets try to roll our own.

+

How would you write append(X,Y,Z) in Prolog?

+

Well, the base case is that if X is empty then, Z is just Y.

+
  append([], Y, Y).         % base case
+

The recursive case is when X is of the form [H|Tx], in which case, Z must begin with H, and the tail of Z is obtained by appending Tx to Y:

+
  append([H|T], Y, [H|Tz]) :- append(Tx,Y,Tz).     % recursive case
+

Very different way of thinking than imperative languages. Lets see what happens with the query: ?- append([1],[2],Z).

+
 Prolog tries to prove the term append([1],[2],Z)
+ ---> recursive case fires.
+ H unifies to 1
+ Tx unifies to []
+ Y unifies to [2]
+ Z unifies to [1|Tz]
+   ---> prove:      append(Tx,Y,Tz)
+   ---> i.e. prove: append([], [2], Tz)
+   base case fires.
+          ---> Tz unifies to [2]
+          ---> therefore:  Z = [1,2]
+

But, because of the magic of pattern matching, proving and predicates, you get the backwards computations by the same “proving” process !

+

Lets do a few more. Lets write a predicate tailof(X,Y) which is true if Y is the tail of the list X.

+
tailof([_|X],X).
+

Again, note the judicious use of the wildcard "_". If you actually named the variable there, eg.

+
tailof([H|X],X).
+

The compiler would warn you that you named a variable but used it only once – a “Singleton Variable”.

+

Lets write a predicate which is true of lists with three or more elements:

+
has3orMoreElements([_,_,_|_]).
+

What does this predicate do ?

+
foo([X,_,_,_,X|_]).
+

One more tricky one. Lets write a predicate isin(X,L) which is true if X is an element of the list L. How ?

+

base case: if X is the first element of the list L.

+
isin(X,[X|_]).
+

recursive case: if X appears in the tail of the list L.

+
isin(X,[_|T]) :- isin(X,T).
+

Let’s give it a spin:

+
?- isin(2,[1,2,3]).
+Yes
+
+?- isin(X,[1,2]).
+X=1 ;
+X=2 ;
+No
+
+?- isin(1,[2,3]).
+No
+

Let’s write another predicate:

+
mylength(L,X)
+

which is true if X is the length of list L

+

mylength([],0). mylength([_|Tail],Len) :- mylength(Tail,TailLen), Len is TailLen +1.

+

?- mylength([1,2],L). does not unify with mylength([],0) unifies with mylength([_|Tail],Len) with the bindings: Tail = [2] and Len = L

+
   now I need to prove the two things:
+     mylength([2],TailLen)   and  Len is TailLen + 1
+
+     can I prove the first one?
+     mylength([2],TailLen)  does not unify with mylength([],0)
+     mylength([2],TailLen)  unifies with  mylength([_|Tail'],Len')
+           with the bindings:
+      Tail' = []   and Len' = TailLen
+
+    now I need to prove the two conditions:
+          mylength([],TailLen'') and Len' is TailLen'' + 1
+          can I prove the first one?
+          mylength([],TailLen'') unifies with mylength([],0)
+             with the bindings:  TailLen'' = 0
+          Len' is TailLen'' + 1  then leads to the binding
+             Len' = 1
+          therefore TailLen is equal to Len', and thus to 1
+        therefore Len is equal to Len' + 1, and thus to 2
+      therefore L is equal to Len, and thus to 2
+    Prolog answers L = 2.
+

———————————– Cuts ———————————-

+
    +
  • Ordering clauses and goals is a way to somewhat control the search and backtracking process, but it is very limited.
  • +
  • There is something called a “cut” that prevents Prolog from backtracking.
  • +
  • Example: Let’s say we’re writing a program to compute the following step function: X < 3 phi(X) = 0 3 <= X < 6 phi(X) = 2
    +6 <= X phi(X) = 4
  • +
+

In Prolog we can implement this with a binary predicate, f(X,Y), which is true if Y is the function value at point X. For instance, f(0,0) is true, f(4,2) is true, but f(2,4) is false. Here is the program:

+
   f(X,0) :- X < 3.                  [rule 1]
+   f(X,2) :- 3 =< X, X < 6.          [rule 2]     note '=<'
+   f(X,4) :- 6 =< X.                 [rule 3]
+

There are two sources of inefficiency in this program, that we’ll see on one example:

+
 ?- f(1,Y), 2 < Y.      [find a Y such that Y = f(1)  and 2 < Y]  
+                        [ we can see this is going to fail]
+

what does Prolog do?

+
                f(1,Y)
+                2 < Y ----------
+     rule 1  /    \             \
+     Y = 0  /      \  rule 2     \  rule 3
+           /        | Y = 2       |  Y = 4
+          |         |             |
+        1 < 3      3 <= 1        6 <= 1
+        2 < 0      1 < 6         2 < 4
+         |         2 < 2          NO
+         |         NO
+        2 < 0      
+         NO
+

There is really no point in trying rule 2 and rule 3 because since X < 3, we know that rule 2 and rule 3 will fail. Basically, the three rules are mututally exclusive. We know that. Prolog doesn’t.

+

So, we can “cut” the backtracking by using the ‘!’ operator:

+
   f(X,0) :- X < 3, !.   
+   f(X,2) :- 3 <= X, X < 6, !.
+   f(X,4) :- 6 <= X.      
+

The new execution looks like:

+
                f(1,Y)
+                2 < Y
+     rule 1  /    
+     Y = 0  /    
+           /    
+          |    
+        1 < 3
+        2 < 0
+         |      
+         CUT
+         |   
+        2 < 0      
+         NO
+
+Lessons: cuts can be used to prevent Prolog from going into branches
+         of the search tree that we know, due to our understanding and
+         knowledge of the problem, will not succedd anyway.
+
    +
  • There are many more things possible with cuts and using them well is an art. A program with no cuts at all will run orders of magnitude slower than an equivalent program with a few ‘!’ thrown in.
  • +
+

————————– Accumulators —————————

+
    +
  • There are cases in which you want to add an argument to a predicate just to keep track of useful information
  • +
+

Example: List Reverse:

+

We will now write a predicate:

+
rev(X,Y)
+

that is true if the list Y is the reverse of the list X. To do so, we will use an accumulator that tracks the elements seen so far in X.

+
rev(X,Y) :- acc_rev(X,Y,[]).
+

The third parameter is the “accumulator”. We will “push” elements into it in the order they appear in X. Thus, when we have pushed all the elements, the third parameter is the “reversed” version of X.

+
acc_rev([],Y,Y).  %base case
+acc_rev([H|T],Y,SoFar) :- acc_rev(T,Y,[H|SoFar]). %recursive case
+
+
+?- rev([1,2,3],Y).
+Y = [3,2,1]
+Yes
+
+?- rev(X,[3,2,1]).
+X = 1,2,3
+Yes
+

The nice thing about predicates, is one can go forwards or backwards! Its completely symmetric…

+

Example: Finding all solutions:

+

Suppose we have a predicate foo(X), defined:

+
  foo(a).  
+  foo(b).
+  foo(c).
+  foo(d).
+

and say we want to find all the terms X such that foo(X) is true. We will use an accumulator to define a predicate:

+
allfoos(L)
+

that is true for a list of terms iff every term in the list satisfies foo.

+
  allfoos(L) :- listallfoos(L,[]).
+

listallfoos is a helper predicate, whose second argument is an accumulator that “tracks” which terms satisfying foo are already known. We shall then “add” those terms that satisfy foo, but are not in the “accumulator”.

+
  % recursive case
+  listallfoos([X|L],SoFar) :- foo(X),    
+                              not(isin(X,SoFar)),
+                              append(SoFar,[X],NewSoFar),
+                              listallfoos(L,NewSoFar).
+  % base case
+  listallfoos([],_).    
+
+  ?- allfoos(A).
+    must prove listalllfoos(A,[]).
+    unifies with listallfoos([X|L],[])
+      must prove four things: foo(X)
+                              not(isin(X,[])
+                              append([],[X],NewSoFar)
+                              listallfoos(L,NewSoFar)
+        foo(a) 			true   (X is bound to a)
+        not(isin(a,[])) 		true
+        append([],[a],NewSoFar)	true with NewSoFar=[a]
+        must prove listallfoos(L,[a])
+        unifies with liastallfoos([Y|L'],[a])
+         must prove four things: foo(Y)
+                                 not(isin(Y,[a]).
+                                 append([a],[Y],NewSoFar')
+                                 listallfoos(L',NewSoFar')
+           foo(a)		  true
+           not(isin(a,[a]))   false  BACKTRACK
+           foo(b)		  true  (Y is bound to b)
+       not(isin(b,[b]))   true
+           append([a],[b],NewSoFar')  true with NewSoFar' = [a,b]
+           must prove listallfoos(L',[a,b])
+           unifies with listallfoos([Z|L''],[a,b])
+             must prove four things: foo(Z)
+                                     not(isin(Z,[a,b])
+             one can see that will fail   BACKTRACK
+           unifies with listallfoos([],[a,b]).
+           therefore: L' unifies with []
+         therefore: [Y|L'] unifies with [b]
+         therefore L unifies with [b]
+         therefore [X|L] unifies with [a,b]
+       therefore A unifies with [a,b]
+     therefore  allfoos([a,b])
+

If you try this code, and hit ‘;’, you’ll get multiple answers Try to figure out why (using the “trace” mode) Solution: add a cut

+
  listallfoos_cut([X|L],SoFar) :-
+        foo(X),    
+        not(isin(X,SoFar)),
+        append(SoFar,[X],NewSoFar),
+        listallfoos(L,NewSoFar),!.
+
+  listallfoos_cut([],_).
+

What happens when you flip order of base case and recursion ?

+

—————————- Puzzle Solving —————————-

+

We now have a good feel for what Prolog programs look like. Lets finish, by seeing how succinctly and elegantly prolog allows us to write code to solve tricky logical puzzles.

+

Towers of Hanoi:

+
              |            |           |
+             =|=           |           |
+           ===|===         |           |
+         =====|=====       |	         |
+         =======|=======     |           |
+   --------------------------------------------------		
+	Peg 1        Peg 2       Peg 3
+

Puzzle: There are three pegs. On the first one, there is a stack of rings of decreasing radius (that forms a tower). In each step, you are allowed to move the top ring from one peg to another, but only if the rings in the new peg form a conical tower – i.e. as long as the sequences of radii from top to bottom is increasing (as shown in the figure).

+
    +
  • Goal: Find the sequence of moves (move top ring from from peg X to peg Y) that moves the tower from peg 1 to peg 3.

  • +
  • The basic action is to move 1 disk, with printing

    +

    move(A,B) :- nl, write (‘Move topdisk from’), write(A), write(’ to ’), write(B).

  • +
  • the main predicate is transfer(N,A,B,X): represents “Move N disks from peg A to peg B by using peg X as a helper”

  • +
  • base case: transfer(1,A,B,X) :- move(A,B). Better written as: transfer(1,A,B,_) :- move(A,B).

  • +
  • inductive case: transfer(N,A,B,X) :- transfer the top N-1 disks to X transfer the (bottom) disk from A to B transfer the top N-1 disks from X to B

    +

    transfer(N,A,B,X) :- M is N-1, transfer(M,A,X,B), move(A,B), transfer(M,X,B,A).

  • +
+

Lets see how these work – lets name the pegs using atoms: peg1, peg2, peg3. Here’s the sequence of moves to transfer a tower of size 3 across (you can try this at home!).

+

?- transfer(3,peg1,peg3,peg2). Move topdisk from peg1 to peg3 Move topdisk from peg1 to peg2 Move topdisk from peg3 to peg2 Move topdisk from peg1 to peg3 Move topdisk from peg2 to peg1 Move topdisk from peg2 to peg3 Move topdisk from peg1 to peg3

+

Farmer/ Wolf / Goat / Cabbage:

+
 *West*              *East*
+
+   Goat       |       |           Goat eats cabbage if no farmer
+   Wolf       | river |           Wolf eats goat if no farmer
+   Cabbage    |       |           Only one spot on the boat(farmer+1)
+

Configure the “state” of the program as a list with the location of the four objects (farmer, wolf, goat, cabbage). There are two locations: West (w) and East (e).

+
   initial state:        [w,w,w,w]
+
+   desired state:        [e,e,e,e]
+

There are four kinds of moves: with the cabbage, (move_cabbage) with the goat, (move_goat) with the wolf, (move_wolf) with nothing. (move_nothing)

+

Here is how the “state” changes with a given move:

+

For instance, [w,w,w,w] —“move wolf”–> [e,e,w,w]

+

We encode this as:

+
   move([w,w,w,w],move_wolf,[e,e,w,w]).
+
+      (this just says that the state transformation above is true)
+

We could write all the possible moves as facts, but there would be a lot. However, it is clear that when the farmer and wolf move, the goat and the cabbage do not move, so a more general fact is:

+
   move([w,w,P_Goat,P_Cabbage],move_wolf,[e,e,P_Goat,P_Cabbage]).
+   move([e,e,P_Goat,P_Cabbage],move_wolf,[w,w,P_Goat,P_Cabbage]).
+

therefore, a more general goal is:

+
   move([X,X,P_Goat,P_Cabbage],move_wolf,[Y,Y,P_Goat,P_Cabbage]) :- change(X,Y)
+

where we have: change(e,w). change(w,e).

+

In addition to ensuring that X and Y are different, this predicate ensures that they cannot be any random atom – they must be either “e” or “w”.

+

Now we can just write the whole program:

+
   move([X,X,P_Goat,P_Cabbage],move_wolf,[Y,Y,P_Goat,P_Cabbage]) :- change(X,Y).
+   move([X,P_Wolf,X,P_Cabbage],move_goat,[Y,P_Wolf,Y,P_Cabbage]) :- change(X,Y).
+   move([X,P_Wolf,P_Goat,X],move_cabbage,[Y,P_Wolf,P_Goat,Y]) :- change(X,Y).
+   move([X,P_Wolf,P_Goat,P_Cabbage],move_nothing,[Y,P_Wolf,P_Goat,P_Cabbage]) :- change(X,Y).
+

At this point we have encoded all the possible moves. But there is nothing about moves being safe or unsafe. We need a safe predicate that takes a state as input and is true if the state is sage (nobody eats nobody).

+

“if at least one of the goat or the wolf is on the same bank as the farmer, AND at least one of the goat or cabbage is on the same bank as the farmer, then we’re safe”

+

We define the one_equal(X,Y,Z) predicate that returns true if at least one of Y or Z is equal to X:

+
one_equal(X,X,_).
+one_equal(X,_,X).
+

then we can have:

+
 safe([P_Farmer,P_Wolf,P_Goat,P_Cabbage]) :-
+     one_equal(P_Farmer,P_Goat,P_Wolf),
+ one_equal(P_Farmer,P_Goat,P_Cabbage).
+

this encodes the logical statement we made above.

+

A solution is defined as a sequence of moves such that we are either: – in the target configuration, all on the east bank, or, – in a state from which there is a single move, which takes us into a safe state, from which we can get to the target configuration.

+
solution([e,e,e,e],[]).
+solution(State,[FirstMove|RemainingMoves]) :-
+   move(State,FirstMove,NextState),
+   safe(NextState),
+   solution(NextState,RemainingMoves).
+

The program is complete.

+

Example run:

+

If you just type solution([w,w,w,w],X), we get into an infinite loop as there are infinitely solutions. So:

+
?- length(X,7), solution([w,w,w,w],X).
+
+ X = [goat, nothing, wolf, goat, cabbage, nothing, goat]
+

In fact, 7 steps is the shortest solution.

+
?- length(X,12), solution([w,w,w,w],X).         (needs an odd number of moves)
+ No
+
+?- length(X,13423), solution([w,w,w,w],X).
+ [goat, goat, goat, goat, goat, goat, goat, ....., +7 steps] is one of the solutions
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/links.html b/docs/links.html new file mode 100644 index 0000000..b7b9c7f --- /dev/null +++ b/docs/links.html @@ -0,0 +1,357 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cse130 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+
+
+

Resources

+ + + + +
+
+
+
+
+ + + + +
+
+
+
+

Text

+

There is no text for CSE 130. We will be using the lecture notes, and other course materials such as aticles, web sites, tutorials, etc. will be made available on this site as appropriate.

+

Below you can find a list of books and online resources that explore various class topics in more depth.

+

Lambda Calculus

+ +

Haskell

+

Books

+ +

Other Resources

+ +

IDE Support

+ +

Prolog

+ +

Fun Articles

+ +
+
+
+
+
+ +
+ + + + + + + + + + + + + + + diff --git a/docs/pin b/docs/pin new file mode 100644 index 0000000..3228b5f --- /dev/null +++ b/docs/pin @@ -0,0 +1 @@ +this is a pin. diff --git a/docs/static/arith/06-parsing.md b/docs/static/arith/06-parsing.md new file mode 100644 index 0000000..645873e --- /dev/null +++ b/docs/static/arith/06-parsing.md @@ -0,0 +1,820 @@ +--- +title: Lexing and Parsing +date: 2018-05-16 +headerImg: books.jpg +--- + +[Token] -> AExp + + + + + +## Plan for this week + + +**Last week:** + +- How do we *evaluate* a program given its AST? + +```haskell +eval :: Env -> Expr -> Value +``` + +**This week:** + +- How do we *convert* program text into an AST? + +```haskell +parse :: String -> Expr +``` + +
+
+
+
+
+
+
+
+
+ +## Example: calculator with variables + +AST representation: + +```haskell +data Aexpr + = AConst Int + | AVar Id + | APlus Aexpr Aexpr + | AMinus Aexpr Aexpr + | AMul Aexpr Aexpr + | ADiv Aexpr Aexpr +``` + +
+ +Evaluator: + +```haskell +eval :: Env -> Aexpr -> Value +... +``` + +
+ +Using the evaluator: + +```haskell +λ> eval [] (APlus (AConst 2) (AConst 6)) +8 + +λ> eval [("x", 16), ("y", 10)] (AMinus (AVar "x") (AVar "y")) +6 + +λ> eval [("x", 16), ("y", 10)] (AMinus (AVar "x") (AVar "z")) +*** Exception: Error {errMsg = "Unbound variable z"} +``` + +
+ +But writing ASTs explicitly is really tedious, +we are used to writing programs as text! + +
+ +We want to write a function that converts strings to ASTs if possible: + +```haskell +parse :: String -> Aexpr +``` + +
+ +For example: + +```haskell +λ> parse "2 + 6" +APlus (AConst 2) (AConst 6) + +λ> parse "(x - y) / 2" +ADiv (AMinus (AVar "x") (AVar "y")) (AConst 2) + +λ> parse "2 +" +*** Exception: Error {errMsg = "Syntax error"} +``` + +
+
+
+
+
+
+
+
+
+ + +## Two-step-strategy + +How do I read a sentence "He ate a bagel"? + + * First split into words: `["He", "ate", "a", "bagel"]` + * Then relate words to each other: "He" is the subject, "ate" is the verb, etc + +
+ +Let's do the same thing to "read" programs! + +
+
+
+
+ +### Step 1 (Lexing) : From String to Tokens + +A string is a list of *characters*: + +![Characters](/static/img/info_parser.001a.jpg){#fig:chars width=70%} + +First we aggregate characters that "belong together" +into **tokens** (i.e. the "words" of the program): + +![Tokens](/static/img/info_parser.001b.jpg){#fig:tokens width=70%} + +We distinguish tokens of different kinds based on their format: + +* all numbers: integer constant +* alphanumeric, starts with a letter: identifier +* `+`: plus operator +* etc + +
+
+
+
+ +### Step 2 (Parsing) : From Tokens to AST + +Next, we convert a sequence of tokens into an AST + + * This is hard... + * ... but the hard parts do not depend on the language! + +
+ +**Parser generators** + + * Given the description of the *token format* generates a *lexer* + * Given the description of the *grammar* generates a *parser* + +We will be using parser generators, +so we only care about how to describe the token format and the grammar + +
+
+
+
+
+
+
+
+
+ +## Lexing + +We will use the tool called `alex` to generate the **lexer** + +Input to `alex`: a `.x` file that describes the *token format* + +
+
+
+
+
+
+
+
+
+ +## Tokens + +First we list the kinds of tokens we have in the language: + +```haskell +data Token + = NUM AlexPosn Int + | ID AlexPosn String + | PLUS AlexPosn + | MINUS AlexPosn + | MUL AlexPosn + | DIV AlexPosn + | LPAREN AlexPosn + | RPAREN AlexPosn + | EOF AlexPosn +``` + +
+
+
+
+
+
+
+
+
+ +## Token rules + +Next we describe the format of each kind of token using a rule: + +```haskell + [\+] { \p _ -> PLUS p } + [\-] { \p _ -> MINUS p } + [\*] { \p _ -> MUL p } + [\/] { \p _ -> DIV p } + \( { \p _ -> LPAREN p } + \) { \p _ -> RPAREN p } + $alpha [$alpha $digit \_ \']* { \p s -> ID p s } + $digit+ { \p s -> NUM p (read s) } +``` + +Each line consist of: + + * a *regular expression* that describes which strings should be recognized as this token + * a Haskell expression that generates the token + +You read it as: + + * if at position `p` in the input string + * you encounter a substring `s` that matches the *regular expression* + * evaluate the Haskell expression with arguments `p` and `s` + +
+
+
+
+
+
+
+
+
+ +## Regular Expressions + +A regular expression has one of the following forms: + +* `[c1 c2 ... cn]` matches *any of* the characters `c1 .. cn` + + * `[0-9]` matches *any digit* + * `[a-z]` matches *any lower-case letter* + * `[A-Z]` matches *any upper-case letter* + * `[a-z A-Z]` matches *any letter* + +* `R1 R2` matches a string `s1 ++ s2` where `s1` matches `R1` and `s2` matches `R2` + + * e.g. `[0-9] [0-9]` matches any two-digit string + +* `R+` matches *one or more* repetitions of what `R` matches + + * e.g. `[0-9]+` matches a natural number + +* `R*` matches *zero or more* repetitions of what `R` matches + + +
+
+ +## QUIZ + +Which of the following strings are matched by `[a-z A-Z] [a-z A-Z 0-9]*`? + +**(A)** (empty string) + +**(B)** `5` + +**(C)** `x5` + +**(D)** `x` + +**(E)** C and D + + +
+ +(I) final + + *Answer:* E + +
+
+
+
+
+
+
+
+ +## Back to token rules + +We can **name** some common regexps like: + +```haskell +$digit = [0-9] +$alpha = [a-z A-Z] +``` + +and write `[a-z A-Z] [a-z A-Z 0-9]*` as `$alpha [$alpha $digit]*` + +
+ +```haskell + [\+] { \p _ -> PLUS p } + [\-] { \p _ -> MINUS p } + [\*] { \p _ -> MUL p } + [\/] { \p _ -> DIV p } + \( { \p _ -> LPAREN p } + \) { \p _ -> RPAREN p } + $alpha [$alpha $digit \_ \']* { \p s -> ID p s } + $digit+ { \p s -> NUM p (read s) } +``` + +* When you encounter a `+`, generate a `PLUS` token +* ... +* When you encounter a nonempty string of digits, convert it into an integer and generate a `NUM` +* When you encounter an alphanumeric string that starts with a letter, save it in an `ID token + + +
+
+
+
+
+
+
+
+
+ +## Running the Lexer + +From the token rules, `alex` generates a function `alexScan` which + + * given an input string, find the *longest* prefix `p` that matches one of the rules + * if `p` is empty, it fails + * otherwise, it converts `p` into a token and returns the rest of the string + +We wrap this function into a handy function + +```haskell +parseTokens :: String -> Either ErrMsg [Token] +``` + +which repeatedly calls `alexScan` until it consumes the whole input string or fails + +
+ +We can test the function like so: + +```haskell +λ> parseTokens "23 + 4 / off -" +Right [ NUM (AlexPn 0 1 1) 23 + , PLUS (AlexPn 3 1 4) + , NUM (AlexPn 5 1 6) 4 + , DIV (AlexPn 7 1 8) + , ID (AlexPn 9 1 10) "off" + , MINUS (AlexPn 13 1 14) + ] +``` + +```haskell +λ> parseTokens "%" +Left "lexical error at 1 line, 1 column" +``` + +
+
+
+
+ +## QUIZ + +What is the result of `parseTokens "92zoo"` +(positions omitted for readability)? + +**(A)** Lexical error + +**(B)** `[ID "92zoo"]` + +**(C)** `[NUM "92"]` + +**(D)** `[NUM "92", ID "zoo"]` + +
+ +(I) final + + *Answer:* D + +
+
+
+
+
+
+
+
+ +## Parsing + +We will use the tool called `happy` to generate the **parser** + +Input to `happy`: a `.y` file that describes the *grammar* + +
+
+ +Wait, wasn't this the grammar? + +```haskell +data Aexpr + = AConst Int + | AVar Id + | APlus Aexpr Aexpr + | AMinus Aexpr Aexpr + | AMul Aexpr Aexpr + | ADiv Aexpr Aexpr +``` + +This was *abstract syntax* + +Now we need to describe *concrete syntax* + + * What programs look like when written as text + * and how to map that text into the abstract syntax + + +
+
+
+
+
+
+
+
+ +## Grammars + +A grammar is a recursive definition of a set of trees + + - each tree is a *parse tree* for some string + - *parse* a string `s` = find a parse tree for `s` that belongs to the grammar + +
+
+ +A grammar is made of: + +- **Terminals**: the leaves of the tree (tokens!) + +- **Nonterminals:** the internal nodes of the tree + +- **Production Rules** that describe how to "produce" a non-terminal from terminals and other non-terminals + + + / + / + + / | \ + / | \ | + / | \ | + | | + | | . | | + "10 + 45 - 7" --> (NUM 10) PLUS (NUM 45) MINUS (NUM 7) + + APlus (AConst 10) (AConst 45) + + ::= NUM { AConst $1 } + | PLUS { APlus $1 $3 } + | MINUS { AMinus $1 $3 } + + | { EApp $1 $2 } + + --> APlus (AConst 10) (AConst 10) + + let foo = \x y z -> x + y + z + in + (((foo 10) 20) 30) + + + (EApp (EAPP (EApp (EVar "foo") (ENum 10)) (ENum 20)) (ENum 30)) + + - i.e. what children each nonterminal can have: + +```haskell +Aexpr : -- NT Aexpr can have as children: + | Aexpr '+' Aexpr { ... } -- NT Aexpr, T '+', and NT Aexpr, or + | Aexpr '-' AExpr { ... } -- NT Aexpr, T '-', and NT Aexpr, or + | ... +``` + +
+
+
+
+
+
+
+
+ +## Terminals + +Terminals correspond to the *tokens* returned by the lexer + +In the `.y` file, we have to declare with terminals in the rules +correspond to which tokens from the `Token` datatype: + +```haskell +%token + TNUM { NUM _ $$ } + ID { ID _ $$ } + '+' { PLUS _ } + '-' { MINUS _ } + '*' { MUL _ } + '/' { DIV _ } + '(' { LPAREN _ } + ')' { RPAREN _ } +``` + +* Each thing on the left is terminal (as appears in the production rules) + +* Each thing on the right is a Haskell pattern for datatype `Token` + +* We use `$$` to designate one parameter of a token constructor as the **value** of that token + + * we will refer back to it from the production rules + +
+
+
+
+
+
+
+
+ +## Production rules + +Next we define productions for our language: + +```haskell +Aexpr : TNUM { AConst $1 } + | ID { AVar $1 } + | '(' Aexpr ')' { $2 } + | Aexpr '*' Aexpr { AMul $1 $3 } + | Aexpr '+' Aexpr { APlus $1 $3 } + | Aexpr '-' Aexpr { AMinus $1 $3 } +``` + +The expression on the right computes the *value* of this node + + * `$1 $2 $3` refer to the *values* of the respective child nodes + +
+
+ +**Example:** parsing `(2)` as `AExpr`: + + 1. Lexer returns a sequence of `Token`s: `[LPAREN, NUM 2, RPAREN]` + + 2. `LPAREN` is the token for terminal `'('`, so let's pick production `'(' Aexpr ')'` + + 3. Now we have to parse `NUM 2` as `Aexpr` and `RPAREN` as `')'` + + 4. `NUM 2` is a token for nonterminal `TNUM`, so let's pick production `TNUM` + + 5. The value of this `Aexpr` node is `AConst 2`, since the value of `TNUM` is `2` + + 6. The value of the top-level `Aexpr` node is also `AConst 2` (see the `'(' Aexpr ')'` production) + +
+
+
+
+
+
+
+
+ +## QUIZ + +What is the value of the root `AExpr` node when parsing `1 + 2 + 3`? + +```haskell +Aexpr : TNUM { AConst $1 } + | ID { AVar $1 } + | '(' Aexpr ')' { $2 } + | Aexpr '*' Aexpr { AMul $1 $3 } + | Aexpr '+' Aexpr { APlus $1 $3 } + | Aexpr '-' Aexpr { AMinus $1 $3 } +``` + +**(A)** Cannot be parsed as `AExpr` + +**(B)** `6` + +**(C)** `APlus (APlus (AConst 1) (AConst 2)) (AConst 3)` + +**(D)** `APlus (AConst 1) (APlus (AConst 2) (AConst 3))` + + +
+ +(I) final + + *Answer:* Could be C or D + +
+
+
+
+
+
+
+
+ +## Running the Parser + +First, we should tell the parser that the top-level non-terminal is `AExpr`: + +```haskell +%name aexpr +``` + +From the production rules and this line, `happy` generates a function `aexpr` that tries to parse a sequence of tokens as `AExpr` + +We package this function together with the lexer and the evaluator into a handy function + +```haskell +evalString :: Env -> String -> Int +``` + +
+ +We can test the function like so: + +(I) lecture + + ```haskell + λ> evalString [] "1 + 3 + 6" + 10 + + λ> evalString [("x", 100), ("y", 20)] "x - y" + ??? + + λ> evalString [] "2 * 5 + 5" + ??? + + λ> evalString [] "2 - 1 - 1" + ??? + ``` + +(I) final + + ```haskell + λ> evalString [] "1 + 3 + 6" + 10 + + λ> evalString [("x", 100), ("y", 20)] "x - y" + 80 + + λ> evalString [] "2 * 5 + 5" + 20 + + λ> evalString [] "2 - 1 - 1" + 2 + ``` + + +
+
+
+
+
+
+
+
+
+ +## Precedence and associativity + +```haskell +λ> evalString [] "2 * 5 + 5" +20 +``` + +The problem is that our grammar is **ambiguous**! + +There are multiple ways of parsing the string `2 * 5 + 5`, namely + +- `APlus (AMul (AConst 2) (AConst 5)) (AConst 5)` (good) +- `AMul (AConst 2) (APlus (AConst 5) (AConst 5))` (bad!) + +*Wanted:* tell `happy` that `*` has higher **precedence** than `+`! + +
+
+ + +```haskell +λ> evalString [] "2 - 1 - 1" +2 +``` + +There are multiple ways of parsing `2 - 1 - 1`, namely + +- `AMinus (AMinus (AConst 2) (AConst 1)) (AConst 1)` (good) +- `AMinus (AConst 2) (AMinus (AConst 1) (AConst 1))` (bad!) + +*Wanted:* tell `happy` that `-` is **left-associative**! + +
+
+ +How do we communicate precedence and associativity to `happy`? + +
+
+
+
+ +### Solution 1: Grammar factoring + +We can split the `AExpr` non-terminal into multiple "levels" + +```haskell +Aexpr : Aexpr '+' Aexpr2 + | Aexpr '-' Aexpr2 + | Aexpr2 + +Aexpr2 : Aexpr2 '*' Aexpr3 + | Aexpr2 '/' Aexpr3 + | Aexpr3 + +Aexpr3 : TNUM + | ID + | '(' Aexpr ')' +``` + +Intuition: `AExpr2` "binds tighter" than `AExpr`, and `AExpr3` is the tightest + + +Now I cannot parse the string `2 * 5 + 5` as + +- `AMul (AConst 2) (APlus (AConst 5) (AConst 5))` + +- Why? + +(I) final + + Because the RHS of `*` has to be `AExpr3`, while `5 + 5` is *not* an `AExpr3` (it's an `AExpr`) + + +
+
+
+
+ +### Solution 2: Parser directives + +This problem is so common that parser generators have a special syntax for it! + +```haskell +%left '+' '-' +%left '*' '/' +``` + +What this means: + + - All our operators are left-associative + - Operators on the lower line have higher precedence + +
+
+
+
+
+
+
+
+ +That's all folks! + + + +[0]: https://github.com/ucsd-cse130/arith/blob/master/src/Language/Arith/Types.hs +[1]: https://github.com/ucsd-cse130/arith/blob/master/src/Language/Arith/Parser0.y +[2]: https://github.com/ucsd-cse130/arith/blob/master/src/Language/Arith/Lexer.x +[3]: https://github.com/ucsd-cse130/arith/blob/master/src/Language/Arith/Parser1.y +[4]: https://github.com/ucsd-cse130/arith/blob/master/src/Language/Arith/Parser2.y +[7]: http://en.wikipedia.org/wiki/Regular_expression diff --git a/docs/static/arith/LICENSE b/docs/static/arith/LICENSE new file mode 100644 index 0000000..a525d89 --- /dev/null +++ b/docs/static/arith/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2016 Ranjit Jhala + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/docs/static/arith/Makefile b/docs/static/arith/Makefile new file mode 100644 index 0000000..88d7a3f --- /dev/null +++ b/docs/static/arith/Makefile @@ -0,0 +1,36 @@ +STACK=stack + +UNAME := $(shell uname) +ifeq ($(UNAME), Linux) + FORMAT=aout +else +ifeq ($(UNAME), Darwin) + FORMAT=macho +endif +endif + +COURSE=cs130s +ASGN=hw1 + +test: clean + $(STACK) test + +bin: + $(STACK) build + +clean: + $(STACK) clean + +distclean: clean + rm -rf .stack-work + +tags: + hasktags -x -c lib/ + +turnin: + # rm -rf .stack-work + rm -rf $(ASGN).tgz + tar -zcvf ../$(ASGN).tgz --exclude .stack-work --exclude .git ../$(ASGN) + mv ../$(ASGN).tgz . + turnin -c $(COURSE) ./$(ASGN).tgz + diff --git a/docs/static/arith/README.md b/docs/static/arith/README.md new file mode 100644 index 0000000..b2474c1 --- /dev/null +++ b/docs/static/arith/README.md @@ -0,0 +1,567 @@ +--- +title: A Tutorial On Parsing with Alex and Happy +date: 2018-05-12 +headerImg: books.jpg +--- + +In this note we consider an invaluable programming tool, the +*parser generator*. The problem that we want to solve +is: how do we **parse strings**, that is, convert +(unstructured) strings, the lowest-level representation of a +program text, into (highly structured) representations like +expressions, statements, functions *etc* which can then be +compiled or interpreted. + +Of course, the problem is much more general and arises in +pretty much every large scale system, how do you convert +raw data strings, into structured objects that can be +manipulated by the rest of the system. + +Of course, one can imagine various convoluted algorithms +for extracting structure from strings. Indeed, you may well +think that the conversion routine depends heavily on the +*target* of the conversion! However, it turns out that we +can design a small *domain-specific language* that describes +a large number of the kinds of target structures, and we +will use a *parser generator* that will automatically +convert the structure description into a parsing function! + +# An Arithmetic Interpreter + +As a running example, let us build a small interpreter for +a language of arithmetic expressions, described by the type + +```haskell +data Aexpr + = AConst Int + | AVar String + | APlus Aexpr Aexpr + | AMinus Aexpr Aexpr + | AMul Aexpr Aexpr + | ADiv Aexpr Aexpr +``` + +shown in file [Types.hs][0]. This expression language +is quite similar to what you saw for the random-art +assignment, and we can write a simple recursive +evaluator for it + +```haskell +eval :: Env -> Aexpr -> Value +eval _ (AConst i) = i +eval env (AVar x) = fromMaybe (errUnbound x) (lookup x env) +eval env (APlus e1 e2) = eval env e1 + eval env e2 +eval env (AMinus e1 e2) = eval env e1 - eval env e2 +eval env (AMul e1 e2) = eval env e1 * eval env e2 +eval env (ADiv e1 e2) = eval env e1 `div` eval env e2 +``` + +Here the `env` is a `[(String, Value)]` corresponding to a list +of variables and their corresponding values. Thus, if you run the +above, you would see something like + +```haskell +λ> eval [] (APlus (AConst 2) (AConst 6)) +8 + +λ> eval [("x", 16), ("y", 10)] (AMinus (AVar "x") (AVar "y")) +6 + +λ> eval [("x", 16), ("y", 10)] (AMinus (AVar "x") (AVar "z")) +*** Exception: Error {errMsg = "Unbound variable z"} +``` + +Now it is rather tedious to write expressions like +`APlus (AConst 2) (AConst 6)`, and `AMinus (AVar "x") (AVar "z")`. +We would like to obtain a simple parsing function + +```haskell +parse :: String -> Aexpr +``` + +that converts a string to the corresponding `Aexpr` if possible. +For example, it would be sweet if we could get + +```haskell +λ> parse "2 + 6" +APlus (AConst 2) (AConst 6) +λ> parse "(x - y) / 2" +ADiv (AMinus (AVar "x") (AVar "y")) (AConst 2) +``` + +and so on. Lets see how to get there. + +# Strategy + +We will use a two-step strategy to convert raw strings into structured +data. + +## Step 1 (Lexing) : From String to Tokens + +Strings are really just a list of very low-level characters. +In the first step, we will aggregate the characters into more +meaningful *tokens* that contain more high-level information. +For example, we will can aggregate a sequence of numeric characters +into an integer, and a sequence of alphanumerics (starting with a +lower-case alphabet) into say a variable name. + +Thus, as a result of the lexing phase, we can convert a list of +individual characters + +![Characters](/static/img/info_parser.001a.jpg) + +into a list of *tokens* + +![Tokens](/static/img/info_parser.001b.jpg) + +### Step 2 (Parsing) : From Tokens to Tree + +Next, we will use a special description of the structures we +are trying to generate called a *grammar* to convert the list +of tokens into a tree-like representation of our final structure: + +```haskell +APlus (AConst 229) (AMul (AConst 98) (AVar "x2")) +``` + +The actual algorithms for converting from lists of tokens to +trees are very subtle and sophisticated. We will omit a detailed +description and instead just look at how the structures can +themselves be represented by grammars. + +Next, we get into the details of our the above strategy, by +describing exactly what the lexer and parser (generators) do +in terms of their input and output. + +# Lexers + +We will use the tool called `alex` to automatically obtain +a lexer from a high-level description of what the tokens are and +what what sequences of characters should get mapped to tokens. + +## Tokens + +The file [Lexer.x][2] describes the set of tokens needed +to represent our simple language + +```haskell +data Token + = NUM AlexPosn Int + | ID AlexPosn String + | PLUS AlexPosn + | MINUS AlexPosn + | MUL AlexPosn + | DIV AlexPosn + | LPAREN AlexPosn + | RPAREN AlexPosn + | EOF AlexPosn +``` + +Note that the first two tokens, `NUM` and `ID` also carry values with +them, respectively `Int` and `String`; the others just have a field +of type `AlexPosn` which, roughly speaking, is the source position +at which that token was found. + +## Regular Expressions + +Next, we must describe the sequences of characters that get aggregated +into a particular token. This is done using [regular expressions][7] +defined in the file [Lexer.x][2], which has a sequence of rules +of the form + +```haskell + [\+] { \p _ -> PLUS p } + [\-] { \p _ -> MINUS p } + [\*] { \p _ -> MUL p } + [\/] { \p _ -> DIV p } + \( { \p _ -> LPAREN p } + \) { \p _ -> RPAREN p } + $alpha [$alpha $digit \_ \']* { \p s -> ID p s } + $digit+ { \p s -> NUM p (read s) } +``` + +Each rule is of the form: `| {hs-expr}`. +Intuitively, each regular expression `regexp` +describes a sequence of characters, and when +that sequence is _matched_ in the input string, +the corresponding Haskell expression is evaluated +to obtain the _token_ that corresponds to the match. +Let's see some examples, + +```haskell + [\+] { \p _ -> PLUS p } + [\-] { \p _ -> MINUS p } + [\*] { \p _ -> MUL p } + [\/] { \p _ -> DIV p } + \( { \p _ -> LPAREN p } + \) { \p _ -> RPAREN p } +``` + +- when a character `+`, `-`, `*`, `/` etc. are encountered, + the lexer generates the tokens `PLUS`, `MINUS`, `MUL`, `DIV` + etc. respectively, + +- `[c1 c2 ... cn]` where each `ci` is a character denotes + a regular expression that matches **any of** the characters + in the sequence. Thus, the regexp `[a-zA-Z]` indicates any + of the alphabets lower, or upper case and `[0-9]` denotes + _any of_ the numeric digits + +```haskell + [0-9]+ { \p s -> NUM p (read s) } +``` + +- Thus, `[0 - 9]` denotes a regexp that matches any + digit-character. When you take a regexp and put a + `+` in front of it, i.e. `e+` corresponds to + **one-or-more** repetitions of `e`. + Thus, the regexp `[0-9]+` matches a _non-empty_ + sequence of digit characters! + In the Haskell expression `p` is the source position, and `s` + is the string corresponding to the matching characters; we return + the exact `Int` by computing `read s` which converts the matched + `String` into an `Int`. + +```haskell + [a-z A-Z] [a-z A-Z 0-9 \_ \']* { \p s -> ID p s } +``` + +- `e1 e2` denotes a regexp that matches any string `s` + that can be split into two parts `s1` and `s2` + (s.t. `s == s1 ++ s2`) where `s1` matches `e1` and + `s2` matches `e2`. That is, `e1 e2` is a **sequencing** + regexp that first matches `e1` and then matches `e2`. + +- `e*` corresponds to **zero-or-more repetitions** of `e`. + Thus, `[a-zA-Z][a-z A-Z 0-9 \_ \']*` is a regexp that + matches all strings that (1) begin with an alphabet, + and then have a (possibly empty) + sequence of alpha-numeric characters, or underscore or `'`. + As before, the entire matching string is bound to the + variable `s` and in this case the `ID p s` token is returned indicating that an identifier appeared in the input stream. + +We can tidy up the lexer by **naming** some common regexps nicely, +e.g. writing + +```haskell +$digit = 0-9 +$alpha = [a-zA-Z] +``` + +and then simplifying the rules to: + +```haskell + $alpha [$alpha $digit \_ \']* { \p s -> ID p s } + $digit+ { \p s -> NUM p (read s) } +``` + + +## Running the Lexer + +We can run the lexer directly to look at the sequences +of tokens found. The function `parseTokens` simply +converts an input string into a buffer on which +the actual lexer operates. + +```haskell +λ> parseTokens "23 + 4 / off -" +Right [ NUM (AlexPn 0 1 1) 23 + , PLUS (AlexPn 3 1 4) + , NUM (AlexPn 5 1 6) 4 + , DIV (AlexPn 7 1 8) + , ID (AlexPn 9 1 10) "off" + , MINUS (AlexPn 13 1 14) + ] +``` + +For each token the above shows the *position* at +which the token was found in the input string. + +Note that the the lexer finds *maximal* matches, that is: + +```haskell +λ> parseTokens "92zoo" +Right [NUM (AlexPn 0 1 1) 92, ID (AlexPn 2 1 3) "zoo"] +``` + +Here, when it hits the `z` it knows that the number pattern has ended and +a new variable pattern has begun. Of course, if you give it something that +doesn't match anything, you get an exception + +```haskell +λ> parseTokens "%" +Left "lexical error at 1 line, 1 column" +``` + +# Parsers + +Next, will use the tool called `happy` to automatically obtain +a parser from a high-level description of the target structure +called a **grammar**. (Note: grammars are very deep area of study, +we're going to take a very superficial look here, guided by the +pragmatics of how to convert strings to `Aexpr` values.) + +## Grammars + +A grammar is a recursive definition of a set of trees, comprising + +- **Terminals** (aka _Token Names_) which describe the _leaf_ nodes + of the tree; here the leaf nodes will always be _tokens_ returned + by the lexer, so we specify the _terminals_ as: + +```haskell +%token + TNUM { NUM _ $$ } + ID { ID _ $$ } + '+' { PLUS _ } + '-' { MINUS _ } + '*' { MUL _ } + '/' { DIV _ } + '(' { LPAREN _ } + ')' { RPAREN _ } +``` + +which says that `TNUM` and `ID` are the "terminals" for the `NUM` and `ID` tokens; +and `'+'`, `'-'` etc are the tokens for the `PLUS`, `MINUS` etc. tokens. + +- **Non-terminals** which describe the _internal_ + nodes of the tree, respectively, and are written + by rules of the form: + +```haskell +NonTerm : + | Term-or-nonterm-1 ... Term-or-non-term-n { Hs-Expr } +``` + + that describe the possible configuration of + children of each internal node, together with a Haskell + expression that generates a *value* that is used to + decorate the node. This value is computed from the + values decorating the respective children. + +For example, the following rules for non-terminals define +the grammar for `Arith` expressions: + +```haskell +Aexpr : BinExp { $1 } + | TNUM { AConst $1 } + | ID { AVar $1 } + | '(' Aexpr ')' { $2 } + +BinExp : Aexpr '*' Aexpr { AMul $1 $3 } + | Aexpr '+' Aexpr { APlus $1 $3 } + | Aexpr '-' Aexpr { AMinus $1 $3 } + | Aexpr '/' Aexpr { ADiv $1 $3 } +``` + +Note that the above grammar (almost) directly mimics the +recursive type definition of the expressions. In the above +grammar, the _two_ non-terminals are `Aexpr` and `BinExp` +(we could call them whatever we like, we just picked the +same name for convenience.) + +The rules `AExpr` define it to be one of: + +- a `BinExp` (which will be expressions made out of a binary-operator), or +- a `TNUM` i.e. a concrete number, or +- a `ID` i.e. a variable, or +- an expression surrounded by parentheses. + +The terminals are the tokens we defined earlier, and each +rule corresponds to how you would take the sub-trees (i.e. +sub-expressions) and stitch them together to get bigger trees. + +The line + +```haskell +%name aexpr +``` + +at the top tells `happy` to use the rules for +the _non-terminal_ `AExpr` to generate a function +`aexpr` that _parses_ a `Token` stream into an `AExpr`. + +Next, let us consider each of the rules in turn. + +```haskell + | TNUM { AConst $1 } + | ID { AVar $1 } +``` + +- The base-case rules for `TNUM` and `ID` state that those + (individual) tokens can be viewed as corresponding to `Aexpr` + nodes. Consider the target expression in the curly braces. + Here `$1` denotes the value decorating the 1st (and only!) + element of the corresponding non/terminal- sequence. That is, + for the former (respectively latter) case `$1` the `Int` + (respectively `String` value) associated with the token, + which we use to obtain the base arithmetic expressions via the + appropriate constructors. + +```haskell + | '(' Aexpr ')' { $2 } +``` + +- The last rule allows us to parse parenthesized expressions; + if there is a left-paren token followed by an expresssion + followed by a matching right-paren token, then the whole + sequence is an `Aexpr` node. Notice how the decorated + expression is simply `$2` which decorates the second + element of the sequence, i.e. the (sub) expression being + wrapped in parentheses. + +```haskell +BinExp : Aexpr '*' Aexpr { AMul $1 $3 } + | Aexpr '+' Aexpr { APlus $1 $3 } + | Aexpr '-' Aexpr { AMinus $1 $3 } + | Aexpr '/' Aexpr { ADiv $1 $3 } +``` + +- The recursive case rules, e.g. for the `+` case says that + if there is a token-sequence that is parsed into an `Aexpr` + node, followed by a `+` token, followed by a sequence that + is parsed into an `Aexpr` node, then the **entire** sequence + can be parsed into an `Aexpr` node. + Here `$1` and `$3` refer to the _first_ and _third_ elements of + the sequence, that is, the _left_ and _right_ subexpressions. + The decorated value is simply the super-expression obtained by + applying the `APlus` constructor to the left and right + subexpressions. The same applies to + +## Running the Parser + +Great, lets take our parser out for a spin! First, lets build the different +elements + +```bash +$ cp src/Language/Arith/Parser0.y src/Language/Arith/Parser.y +$ stack build +... +shift/reduce conflicts: 16 +... +``` + +and now we can load up the `ghci` shell with: + +```bash +$ stack ghci +``` + +which puts us in a ghci shell with the parser loaded in: + +```haskell +λ> evalString [] "1 + 3 + 6" +10 + +λ> evalString [("x", 100), ("y", 20)] "x - y" +80 +``` + +And lo! we have a simple calculator that also supports variables. + +## Precedence and Associativity + +Ok, looks like our calculator works fine, but lets try this + +```haskell +λ> evalString [] "2 * 5 + 5" +20 +``` + +Huh?! you would think that the above should yield `15` as `*` has higher +precedence than `+` , and so the above expression is really `(2 * 5) + 5`. +Indeed, if we took the trouble to put the parentheses in, the right thing +happens + +```haskell +λ> evalString [] "(2 * 5) + 5" +15 +``` + +Indeed, the same issue arises with a single operator + +```haskell +λ> evalString [] "2 - 1 - 1" +2 +``` + +What happens here is that the grammar we gave is **ambiguous** +as there are multiple ways of parsing the string `2 * 5 + 5`, namely + +- `APlus (AMul (AConst 2) (AConst 5)) (AConst 5)`, or +- `AMul (AConst 2) (APlus (AConst 5) (AConst 5))` + +We want the former, but `happy` gives us the latter! +Similarly, there are multiple ways of parsing `2 - 1 - 1`, namely + +- `AMinus (AMinus (AConst 2) (AConst 1)) (AConst 1)`, or +- `AMinus (AConst 2) (AMinus (AConst 1) (AConst 1))` + +Again, since `-` is left-associative, we want the former, but +we get the latter! (Incidentally, this is why we got those wierd +grumbles about `shift/reduce conflicts` when we ran `stack build` +above ...) + +There are various ways of adding precedence, one is to hack the +grammar by adding various extra non-terminals, as done here +[Parser2.y][4]. Note how there are no conflicts if you +use that grammar instead. + +However, since this is such a common problem, there is a much +simpler solution, which is to add precedence and associativity +annotations to the .mly file. In particular, let us use the +modified grammar [Parser1.y][3]. + +```haskell +$ cp src/Language/Arith/Parser1.y src/Language/Arith/Parser.y +$ stack build +``` + +check it out, no conflicts this time! The only difference between this +grammar and the previous one are the lines + +```haskell +%left '+' '-' +%left '*' '/' +``` + +This means that all the operators are **left-associative** +so `e1 - e2 - e3` is parsed as if it were `(e1 - e2) - e3`. +Now, after doing + +```bash +$ stack ghci +``` + +we get + +```haskell +λ> evalString [] "2 - 1 - 1" +0 +``` + +Furthermore, we get that addition and subtraction have lower +precedence than multiplication and division (the order of the +annotations matters!) + +```haskell +λ> evalString [] "2 * 5 + 5" +15 +λ> evalString [] "2 + 5 * 5" +27 +``` + +Hence, the multiplication operator has higher precedence than the addition, +as we have grown to expect, and all is well in the world. + +This concludes our brief tutorial, which should suffice for your Nano +programming assignment. However, if you are curious, I encourage you to +look at the documentation for [alex](https://www.haskell.org/alex/) and +[happy](https://www.haskell.org/happy/) for more details. + +[0]: https://github.com/cse130-sp18/arith/blob/master/src/Language/Arith/Types.hs +[1]: https://github.com/cse130-sp18/arith/blob/master/src/Language/Arith/Parser0.y +[2]: https://github.com/cse130-sp18/arith/blob/master/src/Language/Arith/Lexer.x +[3]: https://github.com/cse130-sp18/arith/blob/master/src/Language/Arith/Parser1.y +[4]: https://github.com/cse130-sp18/arith/blob/master/src/Language/Arith/Parser2.y +[7]: http://en.wikipedia.org/wiki/Regular_expression diff --git a/docs/static/arith/Setup.hs b/docs/static/arith/Setup.hs new file mode 100644 index 0000000..9a994af --- /dev/null +++ b/docs/static/arith/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/docs/static/arith/arith.cabal b/docs/static/arith/arith.cabal new file mode 100644 index 0000000..6c5d595 --- /dev/null +++ b/docs/static/arith/arith.cabal @@ -0,0 +1,53 @@ +name: arith +version: 0.1.0.0 +synopsis: Simple demo on how to use happy/alex +license: MIT +license-file: LICENSE +author: Ranjit Jhala +maintainer: jhala@cs.ucsd.edu +category: Language +build-type: Simple +extra-source-files: README.md +cabal-version: >=1.10 + + +Library + ghc-options: -W -threaded + exposed-modules: Language.Arith.Types, + Language.Arith.Eval, + Language.Arith.Parser + Default-Extensions: OverloadedStrings + + -- other-extensions: + build-depends: base, + array, + mtl + + hs-source-dirs: src + default-language: Haskell2010 + build-tools: alex, happy + other-modules: Language.Arith.Lexer + +executable nano + build-depends: base, + mtl, + transformers, + arith + default-language: Haskell2010 + main-is: src/Main.hs + Default-Extensions: OverloadedStrings + +test-suite test + default-language: Haskell98 + type: exitcode-stdio-1.0 + hs-source-dirs: tests + ghc-options: -threaded + Default-Extensions: OverloadedStrings + main-is: Test.hs + build-depends: base, + directory, + filepath, + tasty, + tasty-hunit, + arith + other-modules: Common diff --git a/docs/static/arith/stack.yaml b/docs/static/arith/stack.yaml new file mode 100644 index 0000000..ebfcdd4 --- /dev/null +++ b/docs/static/arith/stack.yaml @@ -0,0 +1,6 @@ +resolver: lts-18.13 +compiler: ghc-8.10.7 +allow-newer: true +packages: +- '.' + diff --git a/docs/static/arith/stack.yaml.lock b/docs/static/arith/stack.yaml.lock new file mode 100644 index 0000000..14fcf76 --- /dev/null +++ b/docs/static/arith/stack.yaml.lock @@ -0,0 +1,12 @@ +# This file was autogenerated by Stack. +# You should not edit this file by hand. +# For more information, please see the documentation at: +# https://docs.haskellstack.org/en/stable/lock_files + +packages: [] +snapshots: +- completed: + size: 586268 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/13.yaml + sha256: d9e658a22cfe8d87a64fdf219885f942fef5fe2bcb156a9800174911c5da2443 + original: lts-18.13 diff --git a/docs/static/arith/stack.yaml.orig b/docs/static/arith/stack.yaml.orig new file mode 100644 index 0000000..3bbb22e --- /dev/null +++ b/docs/static/arith/stack.yaml.orig @@ -0,0 +1,36 @@ +# For more information, see: http://docs.haskellstack.org/en/stable/yaml_configuration.html + +# Specifies the GHC version and set of packages available (e.g., lts-3.5, nightly-2015-09-21, ghc-7.10.2) +resolver: lts-10.0 + +# Local packages, usually specified by relative directory name +packages: +- '.' + +# Packages to be pulled from upstream that are not in the resolver (e.g., acme-missiles-0.3) +extra-deps: +- clock-0.7.2 + +# Override default flag values for local packages and extra-deps +flags: {} + +# Extra package databases containing global packages +extra-package-dbs: [] + +# Control whether we use the GHC we find on the path +# system-ghc: true + +# Require a specific version of stack, using version ranges +# require-stack-version: -any # Default +# require-stack-version: >= 1.0.0 + +# Override the architecture used by stack, especially useful on Windows +# arch: i386 +# arch: x86_64 + +# Extra directories used by stack for building +# extra-include-dirs: [/path/to/dir] +# extra-lib-dirs: [/path/to/dir] + +# Allow a newer minor version of GHC than the snapshot specifies +# compiler-check: newer-minor diff --git a/docs/static/code/02-haskell.md b/docs/static/code/02-haskell.md new file mode 100644 index 0000000..3d6ed4a --- /dev/null +++ b/docs/static/code/02-haskell.md @@ -0,0 +1,1257 @@ +--- +title: Haskell Crash Course Part I +headerImg: sea.jpg +--- + +## From the Lambda Calculus to Haskell + +
+
+
+
+
+
+
+
+
+
+
+
+
+ +## What is Haskell? + +
+ +A **typed**, **lazy**, **purely functional** programming language + +
+ +Haskell = $\lambda$-calculus ++ + + + better syntax + + types + + built-in features + - booleans, numbers, characters + - records (tuples) + - lists + - recursion + - ... + +
+
+
+
+
+
+ +## Programming in Haskell + +**Computation by Calculation** + +
+ +*Substituting equals by equals* + +
+
+
+
+
+
+
+
+
+
+
+
+ +## Computation via Substituting Equals by Equals + +```haskell + + (1 + 3) * (4 + 5) + -- subst 1 + 3 = 4 +==> 4 * (4 + 5) + -- subst 4 + 5 = 9 +==> 4 * 9 + -- subst 4 * 9 = 36 +==> 36 +``` + +
+
+
+
+
+
+
+
+ +## Computation via Substituting Equals by Equals + +**Equality-Substitution** enables **Abstraction** via **Pattern Recognition** + + +
+
+
+
+
+
+
+
+
+
+ +## Abstraction via Pattern Recognition + +**Repeated Expressions** + +``` + 31 * (42 + 56) + 70 * (12 + 95) + 90 * (68 + 12) +``` + +**Recognize Pattern as $\lambda$-function** + +``` +pat = \x y z -> x * ( y + z ) +``` + +**Equivalent Haskell Definition** + +``` +pat x y z = x * ( y + z ) +``` + +**Function Call is Pattern Instance** + +``` +pat 31 42 56 =*> 31 * (42 + 56) =*> 31 * 98 =*> 3038 +pat 70 12 95 =*> 70 * (12 + 95) =*> 70 * 107 =*> 7490 +pat 90 68 12 =*> 90 * (68 + 12) =*> 90 * 80 =*> 7200 +``` + +**Key Idea:** Computation is _substitute_ equals by equals. + +
+
+
+
+
+
+
+
+
+
+ +## Programming in Haskell + +_Substitute Equals by Equals_ + +
+
+ +Thats it! (*Do not* think of registers, stacks, frames etc.) + +
+
+
+
+
+
+
+ +## Elements of Haskell + +![](/static/img/trinity.png){#fig:types .align-center width=60%} + +- Core program element is an **expression** +- Every _valid_ expression has a **type** (determined at compile-time) +- Every _valid_ expression reduces to a _value_ (computed at run-time) + +Ill-typed* expressions are rejected at *compile-time* before execution + +- *like* in Java +- *not like* $\lambda$-calculus or Python ... + + +```haskell +weirdo = 1 0 -- rejected by GHC +``` + +
+
+
+
+
+
+
+
+ +## Why are types good? + +* Helps with program *design* +* Types are *contracts* (ignore ill-typed inputs!) +* Catches errors *early* +* Allows compiler *to generate code* +* Enables compiler *optimizations* + +
+
+
+
+
+
+
+
+
+
+ +## The Haskell Eco-System + +- **Batch compiler:** `ghc` Compile and run large programs + +- **Interactive Shell** `ghci` Shell to interactively run small programs [online](https://repl.it/languages/haskell) + +- **Build Tool** `stack` Build tool to manage libraries etc. + +
+
+
+
+
+
+
+
+ +## Interactive Shell: `ghci` + +```haskell +$ stack ghci + +:load file.hs +:type expression +:info variable +``` + +
+
+
+
+
+
+
+
+ +## A Haskell Source File + +A sequence of **top-level definitions** `x1`, `x2`, ... + +- Each has *type* `type_1`, `type_2`, ... + +- Each defined by *expression* `expr_1`, `expr_2`, ... + +```haskell +x_1 :: type_1 +x_1 = expr_1 + +x_2 :: type_2 +x_2 = expr_2 + +. +. +. + +``` + +
+
+
+
+
+
+
+
+ +## Basic Types + + +```haskell +ex1 :: Int +ex1 = 31 * (42 + 56) -- this is a comment + +ex2 :: Double +ex2 = 3 * (4.2 + 5.6) -- arithmetic operators "overloaded" + +ex3 :: Char +ex3 = 'a' -- 'a', 'b', 'c', etc. built-in `Char` values + +ex4 :: Bool +ex4 = True -- True, False are builtin Bool values + +ex5 :: Bool +ex5 = False +``` + +
+
+
+
+
+
+
+
+ +## QUIZ: Basic Operations + +```haskell +ex6 :: Int +ex6 = 4 + 5 + +ex7 :: Int +ex7 = 4 * 5 + +ex8 :: Bool +ex8 = 5 > 4 + +quiz :: ??? +quiz = if ex8 then ex6 else ex7 +``` + +What is the *type* of `quiz`? + +**A.** `Int` + +**B.** `Bool` + +**C.** Error! + +
+
+
+
+
+
+
+
+ +## QUIZ: Basic Operations + +```haskell +ex6 :: Int +ex6 = 4 + 5 + +ex7 :: Int +ex7 = 4 * 5 + +ex8 :: Bool +ex8 = 5 > 4 + +quiz :: ??? +quiz = if ex8 then ex6 else ex7 +``` + +What is the *value* of `quiz`? + +**A.** `9` + +**B.** `20` + +**C.** Other! + +
+
+
+
+
+
+
+
+ + +## Function Types + +In Haskell, a **function is a value** that has a type + +``` +A -> B +``` + +A function that + +- takes *input* of type `A` +- returns *output* of type `B` + +For example + +```haskell +isPos :: Int -> Bool +isPos = \n -> (x > 0) +``` + +Define **function-expressions** using `\` like in $\lambda$-calculus! + +But Haskell also allows us to put the parameter on the _left_ + +```haskell +isPos :: Int -> Bool +isPos n = (x > 0) +``` + +(Meaning is **identical** to above definition with `\n -> ...`) + +
+
+
+
+
+
+
+
+ + +## Multiple Argument Functions + +A function that + +- takes three _inputs_ `A1`, `A2` and `A3` +- returns one _output_ `B` has the type + +```haskell +A1 -> A2 -> A3 -> B +``` + +For example + +```haskell +pat :: Int -> Int -> Int -> Int +pat = \x y z -> x * (y + z) +``` + +which we can write with the params on the _left_ as + +```haskell +pat :: Int -> Int -> Int -> Int +pat x y z = x * (y + z) +``` + +
+
+
+
+
+
+
+
+ + +## QUIZ + +What is the type of `quiz` ? + +```haskell +quiz :: ??? +quiz x y = (x + y) > 0 +``` + +**A.** `Int -> Int` + +**B.** `Int -> Bool` + +**C.** `Int -> Int -> Int` + +**D.** `Int -> Int -> Bool` + +**E.** `(Int, Int) -> Bool` + +
+
+
+
+
+
+
+
+ +## Function Calls + +A function call is _exactly_ like in the $\lambda$-calculus + +``` +e1 e2 +``` + +where `e1` is a function and `e2` is the argument. For example + +```haskell +>>> isPos 12 +True + +>>> isPos (0 - 5) +False +``` + +
+
+
+
+
+
+
+
+ + +## Multiple Argument Calls + +With multiple arguments, just pass them in one by one, e.g. + +``` +(((e e1) e2) e3) +``` + +For example + +```haskell +>>> pat 31 42 56 +3038 +``` + + +
+
+
+
+
+
+
+
+ + +## EXERCISE + +Write a function `myMax` that returns the *maximum* of two inputs + +```haskell +myMax :: Int -> Int -> Int +myMax = ??? +``` + +When you are done you should see the following behavior: + +```haskell +>>> myMax 10 20 +20 + +>>> myMax 100 5 +100 +``` + +
+
+
+
+
+
+
+
+ + +## EXERCISE + +Write a function `sumTo` such that `sumTo n` evaluates to `0 + 1 + 2 + ... + n` + +```haskell +sumTo :: Int -> Int +sumTo n = ??? +``` + +When you are done you should see the following behavior: + +```haskell +>>> sumTo 3 +6 +>>> sumTo 4 +10 +>>> sumTo 5 +15 +``` + +
+
+
+
+
+
+
+
+ +## How to Return Multiple Outputs? + + + +
+
+
+
+
+
+
+
+ +## Tuples + +A type for packing `n` different kinds of values into a single "struct" + +```haskell +(T1,..., Tn) +``` + +For example + +```haskell +tup1 :: ??? +tup1 = ('a', 5) + +tup2 :: (Char, Double, Int) +tup2 = ('a', 5.2, 7) +``` + +
+
+
+
+
+
+
+
+ +## QUIZ + +What is the type `???` of `tup3`? + +```haskell +tup3 :: ??? +tup3 = ((7, 5.2), True) +``` + + +**A.** `(Int, Bool)` + +**B.** `(Int, Double, Bool)` + +**C.** `(Int, (Double, Bool))` + +**D.** `((Int, Double), Bool)` + +**E.** `(Tuple, Bool)` + + +
+
+
+
+
+
+
+
+ +## Extracting Values from Tuples + +We can **create** a tuple of three values `e1`, `e2`, and `e3` ... + +```haskell +tup = (e1, e2, e3) +``` + +... but how to **extract** the values from this tuple? + +**Pattern Matching** via `case-of` expressions + +```haskell +fst3 :: (t1, t2, t3) -> t1 +fst3 t = case t of + (x1, x2, x3) -> x1 + +snd3 :: (t1, t2, t3) -> t2 +snd3 t = case t of + (x1, x2, x3) -> x2 + +thd3 :: (t1, t2, t3) -> t3 +thd3 t = case t of + (x1, x2, x3) -> x3 +``` + +
+
+
+
+
+
+
+
+ +## QUIZ + +What is the value of `quiz` defined as + +```haskell +tup2 :: (Char, Double, Int) +tup2 = ('a', 5.2, 7) + +snd3 :: (t1, t2, t3) -> t2 +snd3 t = case t of + (x1, x2, x3) -> x2 + +quiz = snd3 tup2 +``` + +**A.** `'a'` + +**B.** `5.2` + +**C.** `7` + +**D.** `('a', 5.2)` + +**E.** `(5.2, 7)` + +
+
+
+
+
+
+
+
+ +## Lists + +Unbounded Sequence of values of type `T` + +```haskell +[T] +``` + +For example + +```haskell +chars :: [Char] +chars = ['a', 'b', 'c'] + +ints :: [Int] +ints = [1, 3, 5, 7] + +pairs :: [(Int, Bool)] +pairs = [(1,True), (2,False)] +``` + +
+
+
+
+
+
+
+
+ +## QUIZ + +What is the type of `things` defined as + +```haskell +things :: ??? +things = [ [1], [2, 3], [4, 5, 6] ] +``` + +**A.** `[Int]` + +**B.** `([Int], [Int], [Int])` + +**C.** `[(Int, Int, Int)]` + +**D.** `[[Int]]` + +**E.** `List` + +
+
+
+
+
+
+
+
+ +## List's Values Must Have The SAME Type! + +The type `[T]` denotes an unbounded sequence of values of type `T` + +Suppose you have a list + +``` +oops = [1, 2, 'c'] +``` + +There is no `T` that we can use + +- As last element is not `Int` +- First two elements are not `Char`! + +**Result: Mysterious Type Error!** + +
+
+
+
+
+
+
+
+ +## Constructing Lists + +There are two ways to construct lists + +```haskell + [] -- creates an empty list + h:t -- creates a list with "head" 'h' and "tail" t +``` + +For example + +```haskell +>>> 3 : [] +[3] + +>>> 2 : (3 : []) +[2, 3] + +>>> 1 : (2 : (3 : [])) +[1, 2, 3] +``` + +**Cons Operator** `:` **is Right Associative** + +`x1 : x2 : x3 : x4 : t` means `x1 : (x2 : (x3 : (x4 : t)))` + +So we can just avoid the parentheses. + +**Syntactic Sugar** + +Haskell lets you write `[x1, x2, x3, x4]` instead of `x1 : x2 : x3 : x4 : []` + +
+
+
+
+
+
+
+
+ +## Functions Producing Lists + +Lets write a function `copy3` that + +- takes an input `x` and +- returns a list with _three_ copies of `x` + +```haskell +copy3 :: ??? +copy3 x = ??? +``` + +When you are done, you should see the following + +```haskell +>>> copy3 5 +[5, 5, 5] + +>>> copy3 "cat" +["cat", "cat", "cat"] +``` + +
+
+
+
+
+
+
+
+ +## Lets write some Functions + + +[A Recipe](https://www.htdp.org/) + +**Step 1: Write some tests** + +**Step 2: Write the type** + +**Step 3: Write the code** + +
+
+
+
+
+
+
+
+
+
+
+ +## PRACTICE: Clone + +Write a function `clone` such that +`clone n x` returns a list with `n` copies of `x`. + + + +**1. Tests** + +When you are done you should see the following behavior + +```haskell +>>> clone 0 "cat" +[] + +>>> clone 1 "cat" +["cat"] + +>>> clone 2 "cat" +["cat", "cat"] + +>>> clone 3 "cat" +["cat", "cat", "cat"] + +>>> clone 3 100 +[100, 100, 100] +``` + +**2. Types** + +```haskell +clone :: ??? +``` + +**3. Code** + +```haskell +clone n x = ??? +``` +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +## How does `clone` execute? + +(Substituting equals-by-equals!) + +```haskell +clone 3 100 + =*> ??? +``` + +
+
+
+
+
+
+
+
+
+
+
+
+
+ + +## EXERCISE: Range + +Write a function `range` such that `range i j` +returns the list of values `[i, i+1, ..., j]` + +```haskell +range :: ??? +range i j = ??? +``` + +**1. Tests** + +```haskell +>>> range 4 3 +[] + +>>> range 3 3 +[3] + +>>> range 2 3 +[2, 3] + +>>> range 1 3 +[1, 2, 3] + +>>> range 0 3 +[0, 1, 2, 3] +``` + +**2. Type** + +```haskell +range :: ??? +``` + +**3. Code** + +```haskell +range = ??? +``` + +
+
+
+
+
+
+
+
+ +## Functions Consuming Lists + +So far: how to *produce* lists. + +**Next** how to *consume* lists! + +
+
+
+
+
+
+
+ + +
+
+
+
+
+
+
+
+ +## EXERCISE + +Lets write a function `firstElem` such that +`firstElem xs` returns the _first_ element +`xs` if it is a non-empty list, and `0` otherwise. + +**HINT:** How to _extract_ values from a list? + + +**1. Tests** + +When you are done you should see the following behavior: + +```haskell +>>> firstElem [] +0 + +>>> firstElem [10, 20, 30] +10 + +>>> firstElem [5, 6, 7, 8] +5 +``` + +**2. Type** +```haskell +firstElem :: ??? +``` + +**3. Code** +```haskell +firstElem = ??? +``` + +
+
+
+
+
+
+
+
+ +## QUIZ + +Suppose we have the following `mystery` function + +```haskell +mystery :: [a] -> Int +mystery l = case l of + [] -> 0 + (x:xs) -> 1 + mystery xs +``` + +What does `mystery [10, 20, 30]` evaluate to? + +**A.** `10` + +**B.** `20` + +**C.** `30` + +**D.** `3` + +**E.** `0` + +
+
+
+
+
+
+
+
+ +## EXERCISE: Summing a List + +Write a function `sumList` such that `sumList [x1, ..., xn]` returns `x1 + ... + xn` + +**1. Tests** + +When you are done you should get the following behavior: + +```haskell +>>> sumList [] +0 + +>>> sumlist [3] +3 + +>>> sumlist [2, 3] +5 + +>>> sumlist [1, 2, 3] +6 +``` + +**2. Type** + +```haskell +sumList :: [Int] -> Int +``` + +**3. Code** + +```haskell +sumList = ??? +``` + +## Functions on lists: `take` + +Let's write a function to `take` first `n` elements of a list `xs`. + +**1. Tests** + +```haskell +-- >>> ??? +``` + +**2. Type** + +```haskell +take :: ???## Some useful library functions + +```haskell +-- | Length of the list +length :: [t] -> Int + +-- | Append two lists +(++) :: [t] -> [t] -> [t] + +-- | Are two lists equal? +(==) :: [t] -> [t] -> Bool +``` + +
+ +You can search for library functions on [Hoogle](https://www.haskell.org/hoogle/)! + +
+
+
+
+
+
+``` + +**3. Code** + +```haskell +take = ??? +``` + +## Some useful library functions + +```haskell +-- | Length of the list +length :: [t] -> Int + +-- | Append two lists +(++) :: [t] -> [t] -> [t] + +-- | Are two lists equal? +(==) :: [t] -> [t] -> Bool +``` + +
+ +You can search for library functions on [Hoogle](https://www.haskell.org/hoogle/)! + +
+
+
+
+
+
+ + +## Recap + +![](/static/img/trinity.png){#fig:types .align-center width=60%} + +- Core program element is an **expression** +- Every _valid_ expression has a **type** (determined at compile-time) +- Every _valid_ expression reduces to a _value_ (computed at run-time) + + +**Execution** + +- Basic values & operators + +- Execution / Function Calls just *substitute equals by equals* + +- Pack data into *tuples* & *lists* + +- Unpack data via *pattern-matching* + diff --git a/docs/static/code/03-datatypes.md b/docs/static/code/03-datatypes.md new file mode 100644 index 0000000..3de9c9e --- /dev/null +++ b/docs/static/code/03-datatypes.md @@ -0,0 +1,1639 @@ +--- +title: Datatypes and Recursion +date: 2018-04-22 +headerImg: books.jpg +--- + +## Plan for this week + +**Last week:** + + * built-in *data types* + - base types, tuples, lists (and strings) + * writing functions using *pattern matching* and *recursion* + +**This week:** + +![](/static/img/trinity.png){#fig:types .align-center width=60%} + + * user-defined *data types* + - and how to manipulate them using *pattern matching* and *recursion* + * more details about *recursion* + + +
+
+
+
+
+
+ + +## Representing complex data + +**Previously, we've seen:** + +* *base* types: `Bool`, `Int`, `Integer`, `Float` + +* some ways to *build up* types: given types `T1, T2` + + - functions: `T1 -> T2` + - tuples: `(T1, T2)` + - lists: `[T1]` + +
+
+ +**Next: Algebraic Data Types:** + +A single, powerful way to type complex data + +* Lets you define *your own* data types + +* Tuples and lists are *special* cases + + +
+
+
+
+
+
+
+
+
+ +## Building data types + +
+ +Three key ways to build complex types/values: + +1. **Product types** (**each-of**): a value of `T` contains a value of `T1` *and* a value of `T2` + +2. **Sum types** (**one-of**): a value of `T` contains a value of `T1` *or* a value of `T2` + +3. **Recursive types**: a value of `T` contains a *sub-value* of the same type `T` + + +
+
+
+
+
+
+
+
+
+ +## Product types + +Tuples can do the job but there are two problems... + +```haskell +deadlineDate :: (Int, Int, Int) +deadlineDate = (1, 28, 2022) + +deadlineTime :: (Int, Int, Int) +deadlineTime = (11, 59, 59) + +-- | Deadline date extended by one day +extendDate :: (Int, Int, Int) -> (Int, Int, Int) +extendDate = ... +``` + +Can you spot them? + +
+
+
+
+
+
+ +### 1. Verbose and unreadable + +A **type synonym** for `T`: a name that can be used interchangeably with `T` + +```haskell +type Date = (Int, Int, Int) +type Time = (Int, Int, Int) + +deadlineDate :: Date +deadlineDate = (1, 28, 2021) + +deadlineTime :: Time +deadlineTime = (11, 59, 59) + +-- | Deadline date extended by one day +extendDate :: Date -> Date +extendDate = ... +``` + +
+
+
+
+
+
+ + +### 2. Unsafe + +We want to catch this error at compile time!!! + +```haskell +extension deadlineTime +``` + +
+ +*Solution:* construct two different **datatypes** + +```haskell +data Date = Date Int Int Int +data Time = Time Int Int Int + ^ ^---^---^---- parameter types + `---------------- constructor name + +deadlineDate :: Date +deadlineDate = Date 2 7 2020 + +deadlineTime :: Time +deadlineTime = Time 11 59 59 +``` + +
+
+
+
+
+
+
+
+
+ +### Record syntax + +Haskell's **record syntax** allows you to *name* the constructor parameters: + + * Instead of + + ```haskell + data Date = Date Int Int Int + ``` + + * you can write: + + ```haskell + data Date = Date + { month :: Int + , day :: Int + , year :: Int + } + ``` + + * then you can do: + + ```haskell + deadlineDate = Date 2 4 2019 + + deadlineMonth = month deadlineDate -- use field name as a function + ``` + +
+
+
+
+
+
+
+
+
+ +## Building data types + +
+ +Three key ways to build complex types/values: + +1. **Product types** (**each-of**): a value of `T` contains a value of `T1` *and* a value of `T2` **[done]** + +2. **Sum types** (**one-of**): a value of `T` contains a value of `T1` *or* a value of `T2` + +3. **Recursive types**: a value of `T` contains a *sub-value* of the same type `T` + +
+
+
+
+
+
+
+
+
+ +## Example: NanoMarkdown + +Suppose I want to represent a *text document* with simple markup + +Each paragraph is either: + + * plain text (`String`) + * heading: level *and* text (`Int` *and* `String`) + * list: ordered? *and* items (`Bool` *and* `[String]`) + +I want to store all paragraphs in a *list* + +```haskell +doc = [ (1, "Notes from 130") -- Level 1 heading + , "There are two types of languages:" -- Plain text + , (True, [ "those people complain about" -- Ordered list + , "those no one uses"]) + ] +``` + +But this *does not type check*!!! + +
+
+
+
+
+
+
+
+
+ +## Sum Types + +Solution: construct a new type for paragraphs +that is a *sum* (*one-of*) the three options! + +Each paragraph is either: + +* plain text (`String`) +* heading: level and text (`Int` and `String`) +* list: ordered? and items (`Bool` and `[String]`) + +```haskell +data Paragraph -- ^ THREE constructors, w/ different parameters + = PText String -- ^ text: plain string + | PHeading Int String -- ^ head: level and text (Int & String) + | PList Bool [String] -- ^ list: ordered? & items (Bool & [String]) +``` + +
+
+
+
+
+
+
+
+
+ +## QUIZ + +```haskell +data Paragraph + = PText String + | PHeading Int String + | PList Bool [String] +``` + +What is the type of `Text "Hey there!"`? i.e. How would GHCi reply to: + +```haskell +>:t (PText "Hey there!") +``` + +**A.** Syntax error + +**B.** Type error + +**C.** `PText` + +**D.** `String` + +**E.** `Paragraph` + +
+ +(I) final + + *Answer:* E + +
+
+
+
+
+
+
+
+ +## Constructing datatypes + +```haskell +data T + = C1 T11 ... T1k + | C2 T21 ... T2l + | ... + | Cn Tn1 ... Tnm +``` + +- `T` is the **new datatype** + +- `C1 .. Cn` are the **constructors** of `T` + +
+ +A **value** of type `T` is + + * *either* `C1 v1 .. vk` with `vi :: T1i` + * *or* `C2 v1 .. vl` with `vi :: T2i` + * *or* ... + * *or* `Cn v1 .. vm` with `vi :: Tni` + +
+ +You can think of a `T` value as a **box**: + + * *either* a box labeled `C1` with values of types `T11 .. T1k` inside + * *or* a box labeled `C2` with values of types `T21 .. T2l` inside + * *or* ... + * *or* a box labeled `Cn` with values of types `Tn1 .. Tnm` inside + +![One-of Types](/static/img/data-box.png){#fig:types .align-center width=90%} + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +## Constructing datatypes: `Paragraph` + +```haskell +data Paragraph + = PText String + | PHeading Int String + | PList Bool [String] +``` + +Apply a constructor = pack some values into a box (and label it) + + * `PText "Hey there!"` + * put `"Hey there!"` in a box labeled `PText` + * `PHeading 1 "Introduction"` + * put `1` and `"Introduction"` in a box labeled `PHeading` + * Boxes have different labels but same type (`Paragraph`) + +![The `Paragraph` Type](/static/img/data-para-type.png){#fig:types .align-center width=90%} + +with example values: + +![The `Paragraph` Type](/static/img/data-para-val.png){#fig:types .align-center width=90%} + +
+
+
+
+
+
+
+
+
+ +## QUIZ + +```haskell +data Paragraph + = PText String + | PHeading Int String + | PList Bool [String] +``` + +What would GHCi say to + +```haskell +>:t [PHeading 1 "Introduction", PText "Hey there!"] +``` + +**A.** Syntax error + +**B.** Type error + +**C.** `Paragraph` + +**D.** `[Paragraph]` + +**E.** `[String]` + +
+ +(I) final + + *Answer:* D + +
+
+
+
+
+
+
+
+ +## Example: NanoMD + +```haskell +data Paragraph + = PText String + | PHeading Int String + | PList Bool [String] +``` + +Now I can create a document like so: + +```haskell +doc :: [Paragraph] +doc = [ PHeading 1 "Notes from 130" + , PText "There are two types of languages:" + , PList True [ "those people complain about" + , "those no one uses" + ]) + ] +``` + +
+
+
+
+
+
+
+
+
+
+
+
+ + +## Problem: How to *Convert* Documents to HTML? + +How to write a function + +```haskell +html :: Paragraph -> String +html p = ??? -- ^ depends on the kind of paragraph! +``` + +
+
+ +How to tell what's in the box? + + * Look at the label! + +
+
+
+
+
+
+
+
+
+ +## Pattern matching + +**Pattern matching** = looking at the label and extracting values from the box + + * we've seen it before + * but now for arbitrary datatypes + +```haskell +html :: Paragraph -> String +html p = case p of + PText str -> ... -- It's a plain text; str :: String + PHeading lvl str -> ... -- It's a heading; lvl :: Int, str :: String + PList ord items -> ... -- It's a list; ord :: Bool, items :: [String] +``` + +
+ +or, we can pull the `case-of` to the "top" as + +```haskell +html :: Paragraph -> String +html (PText str) = ... -- It's a plain text; str :: String +html (PHeading lvl str) = ... -- It's a heading; lvl :: Int, str :: String +html (PList ord items) = ... -- It's a list; ord :: Bool, items :: [String] +``` + + +
+
+
+
+ + +```haskell +html :: Paragraph -> String +html (PText str) -- It's a plain text! Get string + = unlines [open "p", str, close "p"] + +html (PHeading lvl str) -- It's a heading! Get level and string + = let htag = "h" ++ show lvl + in unwords [open htag, str, close htag] + +html (PList ord items) -- It's a list! Get ordered and items + = let ltag = if ord then "ol" else "ul" + litems = [unwords [open "li", i, close "li"] | i <- items] + in unlines ([open ltag] ++ litems ++ [close ltag]) +``` + +
+
+
+
+
+
+ +### Dangers of pattern matching (1) + +```haskell +html :: Paragraph -> String +html (PText str) = ... +html (PList ord items) = ... +``` + +What would GHCi say to: + +```haskell +html (PHeading 1 "Introduction") +``` + +
+ +(I) final + + *Answer:* Runtime error (no matching pattern) +
+
+
+
+
+ +### Dangers of pattern matching (2) + +```haskell +html :: Paragraph -> String +html (PText str) = unlines [open "p", str, close "p"] +html (PHeading lvl str) = ... +html (PHeading 0 str) = html (PHeading 1 str) +html (PList ord items) = ... +``` + +What would GHCi say to: + +```haskell +html (PHeading 0 "Introduction") +``` + +
+ +(I) final + + *Answer:* `PHeading 0 "Introduction"` will be matched by `PHeading lvl str` + +
+
+
+
+
+ +### Dangers of pattern matching + +Beware of **missing** and **overlapped** patterns + +* GHC warns you about *overlapped* patterns +* GHC warns you about *missing* patterns when called with `-W` (use `:set -W` in GHCi) + +
+
+
+
+
+
+
+
+
+ +## Pattern-Match Expression + +*Everything is an expression?* + +![](/static/img/trinity.png){#fig:types .align-center width=60%} + +We've seen: pattern matching in *equations* + +Actually, pattern-match is *also an expression* + +```haskell +html :: Paragraph -> String +html p = case p of + PText str -> unlines [open "p", str, close "p"] + PHeading lvl str -> ... + PList ord items -> ... +``` + +The code we saw earlier was *syntactic sugar* + +```haskell +html (C1 x1 ...) = e1 +html (C2 x2 ...) = e2 +html (C3 x3 ...) = e3 +``` + +is just for *humans*, internally represented as a `case-of` expression + +```haskell +html p = case p of + (C1 x1 ...) -> e1 + (C2 x2 ...) -> e2 + (C3 x3 ...) -> e3 +``` + +
+
+
+
+
+
+
+
+
+ +## QUIZ + +What is the **type of** + +```haskell +let p = Text "Hey there!" +in case p of + PText str -> str + PHeading lvl _ -> lvl + PList ord _ -> ord +``` + +**A.** Syntax error + +**B.** Type error + +**C.** `String` + +**D.** `Paragraph` + +**E.** `Paragraph -> String` + +
+ +(I) final + + *Answer:* B (cases have different types) + +
+
+
+
+
+
+
+
+ +## Pattern matching expression: typing + +The `case` expression + +```haskell +case e of + pattern1 -> e1 + pattern2 -> e2 + ... + patternN -> eN +``` + +has type `T` if + + * each `e1`...`eN` has type `T` + * `e` has some type `D` + * each `pattern1`...`patternN` is a *valid pattern* for `D` + * i.e. a variable or a constructor of `D` applied to other patterns + +The expression `e` is called the **match scrutinee** + +
+
+
+
+
+
+
+
+
+ +## QUIZ + +What is the type of + +```haskell +let p = Text "Hey there!" +in case p of + PText _ -> 1 + PHeading _ _ -> 2 + PList _ _ -> 3 +``` + +**A.** Syntax error + +**B.** Type error + +**C.** `Paragraph` + +**D.** `Int` + +**E.** `Paragraph -> Int` + +
+ +(I) final + + *Answer:* D + +
+
+
+
+
+
+
+
+ +## Building data types + + +
+ +![](/static/img/trinity.png){#fig:types .align-center width=60%} + +
+ +Three key ways to build complex types/values: + +1. **Product types** (**each-of**): a value of `T` contains a value of `T1` *and* a value of `T2` **[done]** + + * Cartesian *product* of two sets: $v(T) = v(T1) \times v(T2)$ + +2. **Sum types** (**one-of**): a value of `T` contains a value of `T1` *or* a value of `T2` **[done]** + + * Union (*sum*) of two sets: $v(T) = v(T1) \cup v(T2)$ + +3. **Recursive types**: a value of `T` contains a *sub-value* of the same type `T` + +
+
+
+
+
+
+
+
+
+ +## Recursive types + +Let's define **natural numbers** from scratch: + +```haskell +data Nat = ??? +``` + +
+
+
+
+
+
+
+ +```haskell +data Nat = Zero | Succ Nat +``` + +A `Nat` value is: + + * either an *empty* box labeled `Zero` + * or a box labeled `Succ` with another `Nat` in it! + +Some `Nat` values: + +```haskell +Zero -- 0 +Succ Zero -- 1 +Succ (Succ Zero) -- 2 +Succ (Succ (Succ Zero)) -- 3 +... +``` + +
+
+
+
+
+
+
+
+
+ +## Functions on recursive types + +**Recursive code mirrors recursive data** + + +### 1. Recursive type as a parameter + +```haskell +data Nat = Zero -- base constructor + | Succ Nat -- inductive constructor +``` + +**Step 1:** add a pattern per constructor + +``` +toInt :: Nat -> Int +toInt Zero = ... -- base case +toInt (Succ n) = ... -- inductive case + -- (recursive call goes here) +``` + +**Step 2:** fill in base case: + +``` +toInt :: Nat -> Int +toInt Zero = 0 -- base case +toInt (Succ n) = ... -- inductive case + -- (recursive call goes here) +``` + +**Step 2:** fill in inductive case using a recursive call: + +``` +toInt :: Nat -> Int +toInt Zero = 0 -- base case +toInt (Succ n) = 1 + toInt n -- inductive case +``` + +
+
+
+
+
+
+
+
+
+ +## QUIZ + +What does this evaluate to? + +```haskell +let foo i = if i <= 0 then Zero else Succ (foo (i - 1)) +in foo 2 +``` + +**A.** Syntax error + +**B.** Type error + +**C.** `2` + +**D.** `Succ Zero` + +**E.** `Succ (Succ Zero)` + +
+ +(I) final + + *Answer:* E + +
+
+
+
+
+
+
+
+ + +### 2. Recursive type as a result + +```haskell +data Nat = Zero -- base constructor + | Succ Nat -- inductive constructor + + +fromInt :: Int -> Nat +fromInt n + | n <= 0 = Zero -- base case + | otherwise = Succ (fromInt (n - 1)) -- inductive case + -- (recursive call goes here) +``` + +
+
+
+
+
+
+ + +## EXERCISE: Putting the two together + +```haskell +data Nat = Zero -- base constructor + | Succ Nat -- inductive constructor + + +add :: Nat -> Nat -> Nat +add n m = ??? + +sub :: Nat -> Nat -> Nat +sub n m = ??? +``` + +
+
+
+
+
+
+
+
+
+ +## EXERCISE: Putting the two together + +```haskell +data Nat = Zero -- base constructor + | Succ Nat -- inductive constructor + + +add :: Nat -> Nat -> Nat +add n m = ??? +``` + +
+
+
+
+
+
+
+
+
+ +```haskell +data Nat = Zero -- base constructor + | Succ Nat -- inductive constructor + + +add :: Nat -> Nat -> Nat +add Zero m = ??? -- base case +add (Succ n) m = ??? -- inductive case +``` + +
+
+
+
+
+
+
+
+
+ +## EXERCISE: Putting the two together + +```haskell +data Nat = Zero -- base constructor + | Succ Nat -- inductive constructor + +sub :: Nat -> Nat -> Nat +sub n m = ??? +``` + +
+
+
+
+
+
+
+
+
+ +```haskell +sub :: Nat -> Nat -> Nat +sub n Zero = ??? -- base case 1 +sub Zero _ = ??? -- base case 2 +sub (Succ n) (Succ m) = ??? -- inductive case +``` + +
+
+
+
+
+
+
+
+
+ + +## Lesson: Recursive code mirrors recursive data + +* Which of **multiple** arguments should you recurse on? + +* Key: Pick the right **inductive strategy**! + +
+ +(easiest if there is a _single_ argument of course...) + +
+
+
+
+
+
+
+
+
+ + +## Example: Calculator + +I want to implement an arithmetic calculator +to evaluate expressions like: + +* `4.0 + 2.9` +* `3.78 – 5.92` +* `(4.0 + 2.9) * (3.78 - 5.92)` + +What is a Haskell datatype to *represent* these expressions? + +```haskell +data Expr = ??? +``` + +
+
+
+
+
+
+
+
+ +```haskell +data Expr = Num Float + | Add Expr Expr + | Sub Expr Expr + | Mul Expr Expr +``` + +
+
+ +We can represent expressions as + +```haskell +e0, e1, e2 :: Expr +e0 = Add (Num 4.0) (Num 2.9) +e1 = Sub (Num 3.78) (Num 5.92) +e2 = Mul e0 e1 +``` + +
+
+
+
+
+
+ +## EXERCISE: Expression Evaluator + +Write a function to *evaluate* an expression. + + +```haskell +-- >>> eval (Add (Num 4.0) (Num 2.9)) +-- 6.9 + +eval :: Expr -> Float +eval e = ??? +``` + +(I) final + + ```haskell + eval :: Expr -> Float + eval (Num f) = f + eval (Add e1 e2) = eval e1 + eval e2 + eval (Sub e1 e2) = eval e1 - eval e2 + eval (Mul e1 e2) = eval e1 * eval e2 + ``` + +
+
+
+
+
+
+
+
+ +## Recursion is... + +Building solutions for *big problems* +from solutions for *sub-problems* + + - **Base case:** what is the *simplest version* of this problem and how do I solve it? + - **Inductive strategy:** how do I *break down* this problem into sub-problems? + - **Inductive case:** how do I solve the problem *given* the solutions for subproblems? + +
+
+
+
+
+
+
+
+ +## Lists + +Lists aren't built-in! They are an *algebraic data type* like any other: + +```haskell +data List + = Nil -- ^ base constructor + | Cons Int List -- ^ inductive constructor +``` + +* List `[1, 2, 3]` is *represented* as `Cons 1 (Cons 2 (Cons 3 Nil))` + +* Built-in list constructors `[]` and `(:)` + are just fancy syntax for `Nil` and `Cons` + +
+
+ +Functions on lists follow the same general strategy: + +```haskell +length :: List -> Int +length Nil = 0 -- base case +length (Cons _ xs) = 1 + length xs -- inductive case +``` + +
+
+
+
+
+
+
+
+
+
+ +## EXERCISE: Appending Lists + +What is the right *inductive strategy* for appending two lists? + +```haskell +-- >>> append (Cons 1 (Cons 2 (Cons 3 Nil))) (Cons 4 (Cons 5 (Cons 6 Nil))) +-- (Cons 1 (Cons 2 (Cons 3 (Cons 4 (Cons 5 (Cons 6 Nil)))))) +``` + +(I) lecture + + ```haskell + append :: List -> List -> List + append xs ys = ?? + ``` + +(I) final + + ```haskell + append :: List -> List -> List + append Nil ys = ys + append (Cons x xs) ys = Cons x (append xs ys) + ``` + + +
+
+
+
+
+
+
+
+
+ +## Trees + +Lists are *unary trees* with elements stored in the nodes: + + +![Lists are unary trees](/static/img/list-tree.png){#fig:types .align-center width=90%} + + +```haskell +data List = Nil | Cons Int List +``` + +How do we represent *binary trees* with elements stored in the nodes? + +![Binary trees with data at nodes](/static/img/tree-data-node.png){#fig:types .align-center width=90%} + +
+
+
+
+
+
+
+
+
+ +## QUIZ: Binary trees I + +What is a Haskell datatype for *binary trees* with elements stored in the nodes? + +![Binary trees with data at nodes](/static/img/tree-data-node.png){#fig:types .align-center width=90%} + +**(A)** `data Tree = Leaf | Node Int Tree` + +**(B)** `data Tree = Leaf | Node Tree Tree` + +**(C)** `data Tree = Leaf | Node Int Tree Tree` + +**(D)** `data Tree = Leaf Int | Node Tree Tree` + +**(E)** `data Tree = Leaf Int | Node Int Tree Tree` + +
+ +(I) final + + *Answer:* C + + +
+
+
+
+
+
+
+
+
+ +![Binary trees with data at nodes](/static/img/tree-data-node.png){#fig:types .align-center width=90%} + +```haskell +data Tree = Leaf | Node Int Tree Tree + +t1234 = Node 1 + (Node 2 (Node 3 Leaf Leaf) Leaf) + (Node 4 Leaf Leaf) +``` + +
+
+
+
+
+
+
+ +## Functions on trees + +```haskell +depth :: Tree -> Int +depth t = ?? +``` + +(I) final + + ```haskell + depth :: Tree -> Int + depth Leaf = 0 + depth (Node _ l r) = 1 + max (depth l) (depth r) + ``` + +
+
+
+
+
+
+
+ +## QUIZ: Binary trees II + +What is a Haskell datatype for *binary trees* with elements stored in the leaves? + +![Binary trees with data at leaves](/static/img/tree-data-leaf.png){#fig:types .align-center width=90%} + + +**(A)** `data Tree = Leaf | Node Int Tree` + +**(B)** `data Tree = Leaf | Node Tree Tree` + +**(C)** `data Tree = Leaf | Node Int Tree Tree` + +**(D)** `data Tree = Leaf Int | Node Tree Tree` + +**(E)** `data Tree = Leaf Int | Node Int Tree Tree` + +
+ +I) final + + *Answer:* D + +
+
+
+
+
+
+
+ +```haskell +data Tree = Leaf Int | Node Tree Tree + +t12345 = Node + (Node (Node (Leaf 1) (Leaf 2)) (Leaf 3)) + (Node (Leaf 4) (Leaf 5)) +``` + + +
+
+
+
+
+
+
+
+
+ + + + +## Why use Recursion? + +1. Often far simpler and cleaner than loops + + - But not always... + +2. Structure often forced by recursive data + +2. Forces you to factor code into reusable units (recursive functions) + +
+
+
+
+
+
+
+
+ + +## Why **not** use Recursion? + +1. Slow + +2. Can cause stack overflow + +
+
+
+
+
+
+
+
+ +## Example: factorial + +```haskell +fac :: Int -> Int +fac n + | n <= 1 = 1 + | otherwise = n * fac (n - 1) +``` + +
+
+ +Lets see how `fac 4` is evaluated: + +```haskell + + ==> <4 * > -- recursively call `fact 3` + ==> <4 * <3 * >> -- recursively call `fact 2` + ==> <4 * <3 * <2 * >>> -- recursively call `fact 1` + ==> <4 * <3 * <2 * 1>>> -- multiply 2 to result + ==> <4 * <3 * 2>> -- multiply 3 to result + ==> <4 * 6> -- multiply 4 to result + ==> 24 +``` +
+
+ +Each *function call* `<>` allocates a frame on the *call stack* + + - expensive + - the stack has a finite size + +Can we do recursion without allocating stack frames? + +
+
+
+
+
+
+
+
+ + +## Tail Recursion + +Recursive call is the *top-most* sub-expression in the function body + + - i.e. no computations allowed on recursively returned value + + - i.e. value returned by the recursive call == value returned by function + +
+
+ +### QUIZ: Is this function tail recursive? + +```haskell +fac :: Int -> Int +fac n + | n <= 1 = 1 + | otherwise = n * fac (n - 1) +``` + +**A.** Yes + +**B.** No + +
+ +(I) final + + *Answer:* B + +
+
+
+
+
+
+
+ +## Tail recursive factorial + +Let's write a tail-recursive factorial! + +(I) lecture + + ```haskell + facTR :: Int -> Int + facTR n = ... + ``` + +**HINT:** Lets first write it with a `loop` + +(I) final + + ```haskell + facTR :: Int -> Int + facTR n = loop 1 n + where + loop :: Int -> Int -> Int + loop acc n + | n <= 1 = acc + | otherwise = loop (acc * n) (n - 1) + ``` + +
+
+
+
+ +Lets see how `facTR` is evaluated: + + +```haskell + + ==> <> -- call loop 1 4 + ==> <<>> -- rec call loop 4 3 + ==> <<<>>> -- rec call loop 12 2 + ==> <<<<>>>> -- rec call loop 24 1 + ==> 24 -- return result 24! +``` + +Each recursive call **directly** returns the result + + - without further computation + + - no need to remember what to do next! + + - no need to store the "empty" stack frames! + +
+
+
+
+
+
+
+
+ +## Why care about Tail Recursion? + +Because the _compiler_ can transform it into a _fast loop_ + +```haskell +facTR n = loop 1 n + where + loop acc n + | n <= 1 = acc + | otherwise = loop (acc * n) (n - 1) +``` + +
+ +```javascript +function facTR(n){ + var acc = 1; + while (true) { + if (n <= 1) { return acc ; } + else { acc = acc * n; n = n - 1; } + } +} +``` + +- Tail recursive calls can be optimized as a **loop** + + - no stack frames needed! + +- Part of the language specification of most functional languages + + - compiler **guarantees** to optimize tail calls + +
+
+
+
+
+ +That's all folks! + diff --git a/docs/static/code/04-hof.md b/docs/static/code/04-hof.md new file mode 100644 index 0000000..bcff7b8 --- /dev/null +++ b/docs/static/code/04-hof.md @@ -0,0 +1,1242 @@ +--- +title: Higher-Order Functions +date: 2018-04-23 +headerImg: books.jpg +--- + +## Plan for this week + +**Last week:** + + * user-defined *data types* + + * manipulating data-types with *pattern matching* and *recursion* + + * how to make recursive functions more efficient with *tail recursion* + +
+
+
+
+
+
+
+
+
+ +## The long arc of history + +Pattern matching is a *very* old PL idea ... + +- Variants of LISP from 1970 by [Fred McBride](https://personal.cis.strath.ac.uk/conor.mcbride/FVMcB-PhD.pdf) + +... but will finally be added to Python 3.10 + +- https://www.python.org/dev/peps/pep-0622/ + +```python +def make_point_3d(pt): + match pt: + case (x, y): + return Point3d(x, y, 0) + case (x, y, z): + return Point3d(x, y, z) + case Point2d(x, y): + return Point3d(x, y, 0) + case Point3d(_, _, _): + return pt + case _: + raise TypeError("not a point we support") +``` + +
+
+
+
+
+
+
+
+
+ +## Plan for this week + +**Last week:** + + * user-defined *data types* + + * manipulating data-types with *pattern matching* and *recursion* + + * how to make recursive functions more efficient with *tail recursion* + +**This week:** + + * code reuse with *higher-order functions* (HOFs) + + * some useful HOFs: `map`, `filter`, and `fold` + +
+
+
+
+
+
+
+
+
+ +## Recursion is good... + +- Recursive code mirrors recursive data + + - Base constructor -> Base case + - Inductive constructor -> Inductive case (with recursive call) + +- But it can get kinda repetitive! + +
+
+
+
+
+
+
+
+
+ + +## Example: evens + +Let's write a function `evens`: + +```haskell +-- evens [] ==> [] +-- evens [1,2,3,4] ==> [2,4] +``` + +```haskell +evens :: [Int] -> [Int] +evens [] = ... +evens (x:xs) = ... +``` + +
+
+
+
+
+
+ +## Example: four-letter words + +Let's write a function `fourChars`: + +```haskell +-- fourChars [] ==> [] +-- fourChars ["i","must","do","work"] ==> ["must","work"] +``` + +```haskell +fourChars :: [String] -> [String] +fourChars [] = ... +fourChars (x:xs) = ... +``` + +
+
+
+
+
+
+
+
+
+ +## Yikes! Most Code is the Same! + +Lets rename the functions to `foo`: + +```haskell +foo [] = [] +foo (x:xs) + | x mod 2 == 0 = x : foo xs + | otherwise = foo xs + +foo [] = [] +foo (x:xs) + | length x == 4 = x : foo xs + | otherwise = foo xs +``` + +Only difference is **condition** + +- `x mod 2 == 0` vs `length x == 4` + +
+
+
+
+
+
+
+
+
+ +## Moral of the day + +
+ +**D.R.Y.** Don't Repeat Yourself! + +
+ +Can we + +* *reuse* the general pattern and + +* *plug-in* the custom condition? + +
+
+
+
+
+
+
+
+
+ +## Higher-Order Functions + +General **Pattern** + + - expressed as a *higher-order function* + - takes plugin operations as *arguments* + +Specific **Operation** + + - passed in as an argument to the HOF + + +
+
+
+
+
+
+ +## The "filter" pattern + +![The `filter` Pattern](/static/img/filter-pattern.png) + +General Pattern + +- HOF `filter` +- Recursively traverse list and pick out elements that satisfy a predicate + +Specific Operations + +- Predicates `isEven` and `isFour` + +![`filter` instances](/static/img/filter-pattern-instance.png) + +**Avoid duplicating code!** + +
+
+
+
+
+
+
+
+
+ +## QUIZ: What is the type of `filter`? + +```haskell +-- evens [1,2,3,4] ==> [2,4] +evens :: [Int] -> [Int] +evens xs = filter isEven xs + where + isEven :: Int -> Bool + isEven x = x `mod` 2 == 0 + +-- fourChars ["i","must","do","work"] ==> ["must","work"] +fourChars :: [String] -> [String] +fourChars xs = filter isFour xs + where + isFour :: String -> Bool + isFour x = length x == 4 +``` + +So what's the type of `filter`? + +```haskell +{- A -} filter :: (Int -> Bool) -> [Int] -> [Int] + +{- B -} filter :: (String -> Bool) -> [String] -> [String] + +{- C -} filter :: (a -> Bool) -> [a] -> [a] + +{- D -} filter :: (a -> Bool) -> [a] -> [Bool] + +{- E -} filter :: (a -> b) -> [a] -> [b] +``` + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + +## Type of `filter` + +```haskell +-- evens [1,2,3,4] ==> [2,4] +evens :: [Int] -> [Int] +evens xs = filter isEven xs + where + isEven :: Int -> Bool + isEven x = x `mod` 2 == 0 + +-- fourChars ["i","must","do","work"] ==> ["must","work"] +fourChars :: [String] -> [String] +fourChars xs = filter isFour xs + where + isFour :: String -> Bool + isFour x = length x == 4 +``` + +For *any* type `a` + +- **Input** a _predicate_ `a -> Bool` and _collection_ `[a]` +- **Output** a (smaller) _collection_ `[a]` + + +```haskell +filter :: (a -> Bool) -> [a] -> [a] +``` + +`filter` *does not care* what the list elements are + +* as long as the predicate can handle them + +`filter` is **polymorphic** (generic) in the type of list elements + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + + +## Example: ALL CAPS! + +Lets write a function `shout`: + +```haskell +-- shout [] ==> [] +-- shout ['h','e','l','l','o'] ==> ['H','E','L','L','O'] +``` + +```haskell +shout :: [Char] -> [Char] +shout [] = ... +shout (x:xs) = ... +``` + +
+
+
+
+
+
+ +## Example: squares + +Lets write a function `squares`: + +```haskell +-- squares [] ==> [] +-- squares [1,2,3,4] ==> [1,4,9,16] +``` + +```haskell +squares :: [Int] -> [Int] +squares [] = ... +squares (x:xs) = ... +``` + +
+
+
+
+
+
+
+
+
+ +## Yikes, Most Code is the Same + +Lets rename the functions to `foo`: + +```haskell +-- shout +foo [] = [] +foo (x:xs) = toUpper x : foo xs + +-- squares +foo [] = [] +foo (x:xs) = (x * x) : foo xs +``` + +
+
+ +Lets **refactor** into the **common pattern** + +```haskell +pattern = ... +``` + +
+
+
+
+
+
+ +## The "map" pattern + +![The `map` Pattern](/static/img/map-pattern.png) + +General Pattern + +- HOF `map` +- Apply a transformation `f` to each element of a list + +Specific Operations + +- Transformations `toUpper` and `\x -> x * x` + +
+
+
+
+ +```haskell +map f [] = [] +map f (x:xs) = f x : map f xs +``` + +Lets refactor `shout` and `squares` + +```haskell +shout = map ... + +squares = map ... +``` + +
+
+
+
+ +![`map` instances](/static/img/map-pattern-instance.png) + +
+
+
+
+
+
+
+
+
+ +## QUIZ + +What is the type of `map`? + +```haskell +map f [] = [] +map f (x:xs) = f x : map f xs +``` + +**(A)** `(Char -> Char) -> [Char] -> [Char]` + +**(B)** `(Int -> Int) -> [Int] -> [Int]` + +**(C)** `(a -> a) -> [a] -> [a]` + +**(D)** `(a -> b) -> [a] -> [b]` + +**(E)** `(a -> b) -> [c] -> [d]` + + +
+
+
+
+
+
+
+
+
+ +```haskell +-- For any types `a` and `b` +-- if you give me a transformation from `a` to `b` +-- and a list of `a`s, +-- I'll give you back a list of `b`s +map :: (a -> b) -> [a] -> [b] +``` + +
+ +**Type says it all!** + +* The only meaningful thing a function of this type can do is apply its first argument to elements of the list + +* Hoogle it! + +
+ +Things to try at home: + + * can you write a function `map' :: (a -> b) -> [a] -> [b]` whose behavior is different from `map`? + + * can you write a function `map' :: (a -> b) -> [a] -> [b]` + such that `map' f xs` returns a list whose elements are not in `map f xs`? + +
+
+
+
+
+
+
+
+
+ +## QUIZ + +What is the value of `quiz`? + +```haskell +map :: (a -> b) -> [a] -> [b] + +quiz = map (\(x, y) -> x + y) [1, 2, 3] +``` + +**(A)** `[2, 4, 6]` + +**(B)** `[3, 5]` + +**(C)** Syntax Error + +**(D)** Type Error + +**(E)** None of the above + +
+
+
+
+
+
+
+
+
+ +## Don't Repeat Yourself + +Benefits of **factoring** code with HOFs: + +- Reuse iteration pattern + + - think in terms of standard patterns + + - less to write + + - easier to communicate + +- Avoid bugs due to repetition + +
+
+
+
+
+
+
+
+
+ +## Recall: length of a list + +```haskell +-- len [] ==> 0 +-- len ["carne","asada"] ==> 2 +len :: [a] -> Int +len [] = 0 +len (x:xs) = 1 + len xs +``` + +
+
+
+
+
+
+
+
+
+
+ + + +## Recall: summing a list + +```haskell +-- sum [] ==> 0 +-- sum [1,2,3] ==> 6 +sum :: [Int] -> Int +sum [] = 0 +sum (x:xs) = x + sum xs +``` + +
+
+
+
+
+
+
+
+
+
+ +## Example: string concatenation + +Let's write a function `cat`: + +```haskell +-- cat [] ==> "" +-- cat ["carne","asada","torta"] ==> "carneasadatorta" +cat :: [String] -> String +cat [] = ... +cat (x:xs) = ... +``` + +
+
+
+
+
+
+
+
+
+
+
+
+ +## Can you spot the pattern? + +```haskell +-- len +foo [] = 0 +foo (x:xs) = 1 + foo xs + +-- sum +foo [] = 0 +foo (x:xs) = x + foo xs + +-- cat +foo [] = "" +foo (x:xs) = x ++ foo xs +``` + +
+ +```haskell +pattern = ... +``` + +
+
+
+
+
+
+
+
+
+ +## The "fold-right" pattern + +![The `foldr` Pattern](/static/img/foldr-pattern.png) + +General Pattern + +- Recurse on tail +- Combine result with the head using some binary operation + +
+
+
+
+ +```haskell +foldr f b [] = b +foldr f b (x:xs) = f x (foldr f b xs) +``` + +
+
+ +Let's refactor `sum`, `len` and `cat`: + +```haskell +sum = foldr ... ... + +cat = foldr ... ... + +len = foldr ... ... +``` + +Factor the recursion out! + +
+
+
+
+
+
+
+
+
+ + +![`foldr` instances](/static/img/foldr-pattern-instance.png) + +You can write it more clearly as + +```haskell +sum = foldr (+) 0 + +cat = foldr (++) "" +``` + +
+
+
+
+
+
+
+
+
+ + +## The "fold-right" pattern + +```haskell +foldr f b [a1, a2, a3, a4] + ==> f a1 (foldr f b [a2, a3, a4]) + ==> f a1 (f a2 (foldr f b [a3, a4])) + ==> f a1 (f a2 (f a3 (foldr f b [a4]))) + ==> f a1 (f a2 (f a3 (f a4 (foldr f b [])))) + ==> f a1 (f a2 (f a3 (f a4 b))) +``` + +Accumulate the values from the **right** + +For example: + +```haskell +foldr (+) 0 [1, 2, 3, 4] + ==> 1 + (foldr (+) 0 [2, 3, 4]) + ==> 1 + (2 + (foldr (+) 0 [3, 4])) + ==> 1 + (2 + (3 + (foldr (+) 0 [4]))) + ==> 1 + (2 + (3 + (4 + (foldr (+) 0 [])))) + ==> 1 + (2 + (3 + (4 + 0))) +``` + +
+
+
+
+
+
+
+
+
+ +## QUIZ + +What does this evaluate to? + +```haskell +foldr f b [] = b +foldr f b (x:xs) = f x (foldr f b xs) + +quiz = foldr (\x v -> x : v) [] [1,2,3] +``` + +**(A)** Type error + +**(B)** `[1,2,3]` + +**(C)** `[3,2,1]` + +**(D)** `[[3],[2],[1]]` + +**(E)** `[[1],[2],[3]]` + +
+
+
+
+
+
+
+
+
+ +```haskell +foldr (:) [] [1,2,3] + ==> (:) 1 (foldr (:) [] [2, 3]) + ==> (:) 1 ((:) 2 (foldr (:) [] [3])) + ==> (:) 1 ((:) 2 ((:) 3 (foldr (:) [] []))) + ==> (:) 1 ((:) 2 ((:) 3 [])) + == 1 : (2 : (3 : [])) + == [1,2,3] +``` + +
+
+
+
+
+
+
+
+
+ + +## QUIZ + +What is the most general type of `foldr`? + +```haskell + +foldr :: (a -> b -> b) -> b -> [a] -> b +foldr f b [] = b +foldr f b (x:xs) = f x (foldr f b xs) +``` + +**(A)** `(a -> a -> a) -> a -> [a] -> a` + +**(B)** `(a -> a -> b) -> a -> [a] -> b` + +**(C)** `(a -> b -> a) -> b -> [a] -> b` + +**(D)** `(a -> b -> b) -> b -> [a] -> b` + +**(E)** `(b -> a -> b) -> b -> [a] -> b` + +
+ +(I) final + + *Answer:* D + +
+
+
+
+
+
+
+
+
+ + +## Tail Recursive Fold + +```haskell +foldr f b [] = b +foldr f b (x:xs) = f x (foldr f b xs) +``` + +Is `foldr` **tail recursive**? + +(I) final + + *Answer:* No! It calls the binary operations on the results of the recursive call + +
+
+
+
+
+
+
+
+
+ +## What about tail-recursive versions? + +Let's write tail-recursive `sum`! + +```haskell +sumTR :: [Int] -> Int +sumTR = ... +``` +
+
+
+
+
+
+
+
+
+ +Lets run `sumTR` to see how it works + +```haskell +sumTR [1,2,3] + ==> helper 0 [1,2,3] + ==> helper 1 [2,3] -- 0 + 1 ==> 1 + ==> helper 3 [3] -- 1 + 2 ==> 3 + ==> helper 6 [] -- 3 + 3 ==> 6 + ==> 6 +``` + +**Note:** `helper` directly returns the result of recursive call! + +
+
+
+
+
+
+
+
+
+ +Let's write tail-recursive `cat`! + +```haskell +catTR :: [String] -> String +catTR = ... +``` + +
+
+
+
+
+
+
+
+
+ +Lets run `catTR` to see how it works + +```haskell +catTR ["carne", "asada", "torta"] + + ==> helper "" ["carne", "asada", "torta"] + + ==> helper "carne" ["asada", "torta"] + + ==> helper "carneasada" ["torta"] + + ==> helper "carneasadatorta" [] + + ==> "carneasadatorta" +``` + +**Note:** `helper` directly returns the result of recursive call! + +
+
+
+
+
+
+
+
+
+ +## Can you spot the pattern? + +```haskell +-- sumTR +foo xs = helper 0 xs + where + helper acc [] = acc + helper acc (x:xs) = helper (acc + x) xs + + +-- catTR +foo xs = helper "" xs + where + helper acc [] = acc + helper acc (x:xs) = helper (acc ++ x) xs +``` + +
+ +```haskell +pattern = ... +``` + +
+
+
+
+
+
+
+
+
+ +## The "fold-left" pattern + +![The `foldl` Pattern](/static/img/foldl-pattern.png) + +General Pattern + +- Use a helper function with an extra accumulator argument +- To compute new accumulator, combine current accumulator with the head using some binary operation + +
+
+
+
+ +```haskell +foldl f b xs = helper b xs + where + helper acc [] = acc + helper acc (x:xs) = helper (f acc x) xs +``` + +
+
+ +Let's refactor `sumTR` and `catTR`: + +```haskell +sumTR = foldl ... ... + +catTR = foldl ... ... +``` + +Factor the tail-recursion out! + +
+
+
+
+
+
+
+
+ + +## QUIZ + +What does this evaluate to? + +```haskell +foldl f b xs = helper b xs + where + helper acc [] = acc + helper acc (x:xs) = helper (f acc x) xs + +quiz = foldl (\xs x -> x : xs) [] [1,2,3] +``` + +
+ +**(A)** Type error + +**(B)** `[1,2,3]` + +**(C)** `[3,2,1]` + +**(D)** `[[3],[2],[1]]` + +**(E)** `[[1],[2],[3]]` + +
+ +(I) final + + *Answer:* C + +``` +foldl f b (x1: x2: x3 : []) + ==> helper b (x1: x2: x3 : []) + ==> helper (f x1 b) (x2: x3 : []) + ==> helper (f x2 (f x1 b)) (x3 : []) + ==> helper (f x3 (f x2 (f x1 b))) [] + ==> ( x3 : (x2 : (x1 : []))) +``` + + +
+
+
+
+
+
+
+
+ +## The "fold-left" pattern + +```haskell +foldl f b [x1, x2, x3, x4] + ==> helper b [x1, x2, x3, x4] + ==> helper (f b x1) [x2, x3, x4] + ==> helper (f (f b x1) x2) [x3, x4] + ==> helper (f (f (f b x1) x2) x3) [x4] + ==> helper (f (f (f (f b x1) x2) x3) x4) [] + ==> (f (f (f (f b x1) x2) x3) x4) +``` + +Accumulate the values from the **left** + +For example: + +```haskell +foldl (+) 0 [1, 2, 3, 4] + ==> helper 0 [1, 2, 3, 4] + ==> helper (0 + 1) [2, 3, 4] + ==> helper ((0 + 1) + 2) [3, 4] + ==> helper (((0 + 1) + 2) + 3) [4] + ==> helper ((((0 + 1) + 2) + 3) + 4) [] + ==> ((((0 + 1) + 2) + 3) + 4) +``` + +
+
+
+
+
+
+
+
+
+ +## Left vs. Right + +```haskell +foldl f b [x1, x2, x3] ==> f (f (f b x1) x2) x3 -- Left + +foldr f b [x1, x2, x3] ==> f x1 (f x2 (f x3 b)) -- Right +``` + +For example: + +```haskell +foldl (+) 0 [1, 2, 3] ==> ((0 + 1) + 2) + 3 -- Left + +foldr (+) 0 [1, 2, 3] ==> 1 + (2 + (3 + 0)) -- Right +``` + +Different types! + +```haskell +foldl :: (b -> a -> b) -> b -> [a] -> b -- Left + +foldr :: (a -> b -> b) -> b -> [a] -> b -- Right +``` + +
+
+
+
+
+
+
+
+
+ +## Higher Order Functions + +Iteration patterns over collections: + +- **Filter** values in a collection given a *predicate* +- **Map** (iterate) a given *transformation* over a collection +- **Fold** (reduce) a collection into a value, given a *binary operation* to combine results + +
+ +HOFs can be put into libraries to enable modularity + +- Data structure **library** implements `map`, `filter`, `fold` for its collections + + - generic efficient implementation + + - generic optimizations: `map f (map g xs) --> map (f.g) xs` + + +- Data structure **clients** use HOFs with specific operations + + - no need to know the implementation of the collection + +Crucial foundation of + +- "big data" revolution e.g. _MapReduce_, _Spark_, _TensorFlow_ + +- "web programming" revolution e.g. _Jquery_, _Angular_, _React_ + +
+
+
+
+
+
+
+
+
+
+
+ diff --git a/docs/static/code/05-closure.md b/docs/static/code/05-closure.md new file mode 100644 index 0000000..9a16f5e --- /dev/null +++ b/docs/static/code/05-closure.md @@ -0,0 +1,1450 @@ +--- +title: Closures +date: 2019-05-15 +headerImg: books.jpg +--- + +## Past three weeks + +How to *use* essential language constructs? + +- Data Types +- Recursion +- Higher-Order Functions + +## Next two weeks + +How to *implement* language constructs? + +- Local variables and scope +- Environments and Closures + + + +### Interpreter + +How do we *represent* and *evaluate* a program? + + + +
+
+
+
+
+
+
+
+
+ +How should we evaluate this expression? + + +```haskell + eval [] + {let c = 42 in let cTimes = \x -> c * x in cTimes 2} +=> eval [c:42] + {let cTimes = \x -> c * x in cTimes 2} +=> eval [cTimes:???, c:42] + {cTimes 2} +``` + +
+ + +What is the **value** of `cTimes`??? + +
+
+
+
+
+
+
+
+
+ +## Rethinking our values + +**Until now:** a program *evaluates* to an integer (or fails) + +```haskell +type Value = Int + +type Env = [(Id, Value)] + +eval :: Env -> Expr -> Value +``` + +
+
+ +What do these programs evaluate to? + +```haskell +(1) +\x -> 2 * x +==> ??? + +(2) +let f = \x -> \y -> 2 * (x + y) in +f 5 +==> ??? +``` + +(I) final + + Conceptually, (1) evaluates to itself (not exactly, see later). + while (2) evaluates to something equivalent to `\y -> 2 * (5 + y)` + + +
+
+
+
+
+
+ +**Now:** a program evaluates to an integer or *a lambda abstraction* (or fails) + + - Remember: functions are *first-class* values + +
+ +Let's change our definition of values! + +```haskell +data Value = VNum Int + | VLam ??? -- What info do we need to store? + +-- Other types stay the same +type Env = [(Id, Value)] + +eval :: Env -> Expr -> Value +``` +
+
+
+
+
+
+
+
+
+ +## Function values + +How should we represent a function value? + +```haskell +let c = 42 in +let cTimes = \x -> c * x in +cTimes 2 +``` + +We need to store enough information about `cTimes` +so that we can later evaluate any *application* of `cTimes` +(like `cTimes 2`)! + +
+
+ +First attempt: + +```haskell +data Value = VNum Int + | VLam Id Expr -- formal + body +``` + +
+
+ +Let's try this! + +```haskell + eval [] + {let c = 42 in let cTimes = \x -> c * x in cTimes 2} +=> eval [c:42] + {let cTimes = \x -> c * x in cTimes 2} +=> eval [cTimes:(\x -> c*x), c:42] + {cTimes 2} + -- evaluate the function: +=> eval [cTimes:(\x -> c*x), c:42] + {(\x -> c * x) 2} + -- evaluate the argument, bind to x, evaluate body: +=> eval [x:2, cTimes:(\x -> c*x), c:42] + {c * x} +=> 42 * 2 +=> 84 +``` + +
+ +Looks good... can you spot a problem? + +
+
+
+
+
+
+
+
+
+ +## QUIZ + +What should this evaluate to? + +```haskell +let c = 42 in +let cTimes = \x -> c * x in -- but which c??? +let c = 5 in +cTimes 2 +``` + +**(A)** `84` + +**(B)** `10` + +**(C)** Error: multiple definitions of `c` + +
+ +(I) final + + *Answer:* A + +
+
+
+
+
+
+
+
+ +## Static vs Dynamic Scoping + +What we want: + +```haskell +let c = 42 in +let cTimes = \x -> c * x in +let c = 5 in +cTimes 2 + +=> 84 +``` + +**Lexical** (or **static**) scoping: + + - each occurrence of a variable refers to the most recent binding *in the program text* + - definition of each variable is unique and known *statically* + - good for readability and debugging: don’t have to figure out where a variable got "assigned" + + +
+
+ +What we **don't** want: + +```haskell +let c = 42 in +let cTimes = \x -> c * x in +let c = 5 in +cTimes 2 + +=> 10 +``` + +**Dynamic** scoping: + + - each occurrence of a variable refers to the most recent binding *during program execution* + - can't tell where a variable is defined just by looking at the function body + - nightmare for readability and debugging: + +```haskell +let cTimes = \x -> c * x in +let c = 5 in +let res1 = cTimes 2 in -- ==> 10 +let c = 10 in +let res2 = cTimes 2 in -- ==> 20!!! +res2 - res1 +``` + +
+
+
+
+
+
+
+
+
+ +## Function values + +```haskell +data Value = VNum Int + | VLam Id Expr -- formal + body +``` + +This representation can only implement dynamic scoping! + +```haskell +let c = 42 in +let cTimes = \x -> c * x in +let c = 5 in +cTimes 2 +``` + +evaluates as: + +```haskell + eval [] + {let c = 42 in let cTimes = \x -> c * x in let c = 5 in cTimes 2} +=> eval [c:42] + {let cTimes = \x -> c * x in let c = 5 in cTimes 2} +=> eval [cTimes:(\x -> c*x), c:42] + {let c = 5 in cTimes 2} +=> eval [c:5, cTimes:(\x -> c*x), c:42] + {cTimes 2} +=> eval [c:5, cTimes:(\x -> c*x), c:42] + {(\x -> c * x) 2} +=> eval [x:2, c:5, cTimes:(\x -> c*x), c:42] + {c * x} + -- latest binding for c is 5! +=> 5 * 2 +=> 10 +``` + +**Lesson learned:** need to remember what `c` was bound to when `cTimes` was defined! + + - i.e. "freeze" the environment at function definition + +
+
+
+
+
+
+
+
+
+ +## Closures + +To implement lexical scoping, we will represent function values as *closures* + +
+ +**Closure** = *lambda abstraction* (formal + body) + *environment* at function definition + +
+ +```haskell +data Value = VNum Int + | VClos Env Id Expr -- env + formal + body +``` + +
+ +Our example: + +```haskell + eval [] + {let c = 42 in let cTimes = \x -> c * x in let c = 5 in cTimes 2} +=> eval [c:42] + {let cTimes = \x -> c * x in let c = 5 in cTimes 2} + -- remember current env: +=> eval [cTimes:<[c:42], \x -> c*x>, c:42] + {let c = 5 in cTimes 2} +=> eval [c:5, cTimes:<[c:42], \x -> c*x>, c:42] + {cTimes 2} +=> eval [c:5, cTimes:<[c:42], \x -> c*x>, c:42] + {<[c:42], \x -> c * x> 2} + -- restore env to the one inside the closure, then bind 2 to x: +=> eval [x:2, c:42] + {c * x} +=> 42 * 2 +=> 84 +``` + +
+
+
+
+
+
+
+
+
+ +## QUIZ + +Which variables should be saved in the closure environment of `f`? + +```haskell +let a = 20 in +let f = + \x -> let y = x + 1 in + let g = \z -> y + z in + a + g x + in ... +``` + +**(A)** `a` + +**(B)** `a x` + +**(C)** `y g` + +**(D)** `a y g` + +**(E)** `a x y g z` + + +
+ +(I) final + + *Answer:* A + +
+
+
+
+
+
+
+
+ +## Free vs bound variables + +- An occurrence of `x` is **free** if it is not **bound** +- An occurrence of `x` is **bound** if it's inside + - `e2` where `let x = e1 in e2` + - `e` where `\x -> e` +- A closure environment has to save *all free variables* of a function definition! + + +```haskell +let a = 20 in +let f = + \x -> let y = x + 1 in + let g = \z -> y + z in + a + g x -- a is the only free variable! + in ... +``` + +
+
+
+
+
+
+
+
+
+ +## Evaluator + +Let's modify our evaluator to handle functions! + +```haskell +data Value = VNum Int + | VClos Env Id Expr -- env + formal + body + +eval :: Env -> Expr -> Value +eval env (Num n) = VNum n -- must wrap in VNum now! +eval env (Var x) = lookup x env +eval env (Bin op e1 e2) = VNum (f v1 v2) + where + (VNum v1) = eval env e1 + (VNum v2) = eval env e2 + f = ... -- as before +eval env (Let x e1 e2) = eval env' e2 + where + v = eval env e1 + env' = add x v env +eval env (Lam x body) = ??? -- construct a closure +eval env (App fun arg) = ??? -- eval fun, then arg, then apply +``` +
+
+
+
+
+
+
+
+
+ +Evaluating functions: + +* **Construct a closure**: save environment at function definition +* **Apply a closure**: restore saved environment, add formal, evaluate the body + +```haskell +eval :: Env -> Expr -> Value +... +eval env (Lam x body) = VClos env x body +eval env (App fun arg) = eval bodyEnv body + where + (VClos closEnv x body) = eval env fun -- eval function to closure + vArg = eval env arg -- eval argument + bodyEnv = add x vArg closEnv +``` + + +## QUIZ + +With `eval` as defined above, what does this evaluate to? + +```haskell +let f = \x -> x + y in +let y = 10 in +f 5 +``` + +**(A)** `15` + +**(B)** `5` + +**(C)** Error: unbound variable `x` + +**(D)** Error: unbound variable `y` + +**(E)** Error: unbound variable `f` + + +
+ +(I) final + + *Answer:* D + +
+
+
+
+
+
+
+ +```haskell + eval [] + {let f = \x -> x + y in let y = 10 in f 5} +=> eval [f:<[], \x -> x + y>] + {let y = 10 in f 5} +=> eval [y:10, f:<[], \x -> x + y>] + {f 5} +=> eval [y:10, f:<[], \x -> x + y>] + {<[], \x -> x + y> 5} +=> eval [x:5] -- env got replaced by closure env + formal! + {x + y} -- y is unbound! +``` + +
+
+
+
+
+
+
+
+ +## QUIZ + +With `eval` as defined above, what does this evaluate to? + +```haskell +let f = \n -> n * f (n - 1) in +f 5 +``` + +**(A)** `120` + +**(B)** Evaluation does not terminate + +**(C)** Error: unbound variable `f` + +
+ +(I) final + + *Answer:* C + +
+
+
+
+
+
+
+ +```haskell + eval [] + {let f = \n -> n * f (n - 1) in f 5} +=> eval [f:<[], \n -> n * f (n - 1)>] + {f 5} +=> eval [f:<[], \n -> n * f (n - 1)>] + {<[], \n -> n * f (n - 1)> 5} +=> eval [n:5] -- env got replaced by closure env + formal! + {n * f (n - 1)} -- f is unbound! +``` + + +**Lesson learned:** to support recursion, +we need a different way of constructing the closure environment! + +
+
+
+
+
+
+
+
+
+ +## The Nano Language + +Features of Nano: + +1. Arithmetic expressions **[done]** +2. Variables and let-bindings **[done]** +3. Functions **[done]** +4. Recursion **[this is part of HW4]** + + +
+
+
+
+
+
+
+
+ + diff --git a/docs/static/code/05-environments.md b/docs/static/code/05-environments.md new file mode 100644 index 0000000..e0f9a81 --- /dev/null +++ b/docs/static/code/05-environments.md @@ -0,0 +1,1767 @@ +--- +title: Environments +date: 2021-02-16 +headerImg: books.jpg +--- + +## Past three weeks + +How to *use* essential language constructs? + +- Data Types +- Recursion +- Higher-Order Functions + +## Next two weeks + +How to *implement* language constructs? + +- Local variables and scope + +- Environments and Closures + +- _(skip)_ Type Inference + +### Interpreter + +How do we *represent* and *evaluate* a program? + + + +
+
+
+
+
+
+
+
+
+ +## Roadmap: The Nano Language + +Features of Nano: + +1. **Arithmetic** +2. Variables +3. Let-bindings +4. Functions +5. Recursion + +![](/static/img/trinity.png){#fig:types .align-center width=60%} + +
+
+
+
+
+
+
+
+
+ +## 1. Nano: Arithmetic + +A *grammar* of arithmetic expressions: + +```haskell +e ::= n + | e1 + e2 + | e1 - e2 + | e1 * e2 +``` + +
+ +| **Expressions** | | **Values** | +|:----------------|:------|:------------| +| `4` | `==>` | 4 | +| `4 + 12` | `==>` | 16 | +| `(4+12) - 5` | `==>` | 11 | + +
+
+ +## Representing Arithmetic Expressions and Values + +![](/static/img/trinity.png){#fig:types .align-center width=60%} + +Lets *represent* arithmetic expressions as type + +```haskell +data Expr + = ENum Int -- ^ n + | EAdd Expr Expr -- ^ e1 + e2 + | ESub Expr Expr -- ^ e1 - e2 + | EMul Expr Expr -- ^ e1 * e2 +``` + +Lets *represent* arithmetic values as a type + +```haskell +type Value = Int +``` + +
+
+
+
+ + +## Evaluating Arithmetic Expressions + +![](/static/img/trinity.png){#fig:types .align-center width=60%} + +We can now write a Haskell function to *evaluate* an expression: + +```haskell +eval :: Expr -> Value +eval (ENum n) = n +eval (EAdd e1 e2) = eval e1 + eval e2 +eval (ESub e1 e2) = eval e1 - eval e2 +eval (EMul e1 e2) = eval e1 * eval e2 +``` + +
+
+
+
+
+
+
+
+ +## Alternative representation + +Lets pull the *operators* into a separate type + +```haskell +data Binop = Add -- ^ `+` + | Sub -- ^ `-` + | Mul -- ^ `*` + +data Expr = ENum Int -- ^ n + | EBin Binop Expr Expr -- ^ e1 `op` e2 +``` + +
+
+
+
+
+
+
+
+ +## QUIZ + +Evaluator for alternative representation + +```haskell +eval :: Expr -> Value +eval (ENum n) = n +eval (EBin op e1 e2) = evalOp op (eval e1) (eval e2) +``` + +What is a suitable type for `evalOp`? + +```haskell +{- 1 -} evalOp :: BinOp -> Value +{- 2 -} evalOp :: BinOp -> Value -> Value -> Value +{- 3 -} evalOp :: BinOp -> Expr -> Expr -> Value +{- 4 -} evalOp :: BinOp -> Expr -> Expr -> Expr +{- 5 -} evalOp :: BinOp -> Expr -> Value +``` + +
+
+
+
+
+
+
+
+
+ +## The Nano Language + +Features of Nano: + +1. Arithmetic *[done]* +2. **Variables** +3. Let-bindings +4. Functions +5. Recursion + +![](/static/img/trinity.png){#fig:types .align-center width=60%} + +
+
+
+
+
+
+
+
+
+ +## 2. Nano: Variables + +Let's add variables and `let` bindings! + +```haskell +e ::= n -- OLD + | e1 + e2 + | e1 - e2 + | e1 * e2 + -- NEW + | x -- variables +``` + +Lets extend our datatype + +```haskell +type Id = String + +data Expr + = ENum Int -- OLD + | EBin Binop Expr Expr + -- NEW + | EVar Id -- variables +``` + +
+ +## QUIZ + +What should the following expression evaluate to? + +```haskell +x + 1 +``` + +**(A)** `0` + +**(B)** `1` + +**(C)** Error + +
+
+
+
+
+
+
+
+
+ +## Environment + +An expression is evaluated in an **environment** + +- A **phone book** which maps *variables* to *values* + +```haskell +[ "x" := 0, "y" := 12, ...] +``` + +A type for *environments* + +```haskell +type Env = [(Id, Value)] +``` + +
+
+
+
+
+ +## Evaluation in an Environment + +We write + +```haskell +(eval env expr) ==> value +``` + +to mean + +When `expr` is **evaluated in environment** `env` the result is `value` + +So: when we have variables, we modify our evaluator (`eval`) + +- to take an input environment `env` in which `expr` must be evaluated. + +```haskell +eval :: Env -> Expr -> Value +eval env expr = -- ... value-of-expr-in-env... +``` + +First, lets update the evaluator for the arithmetic cases `ENum` and `EBin` + +```haskell +eval :: Env -> Expr -> Value +eval env (ENum n) = ??? +eval env (EBin op e1 e2) = ??? +``` + +
+
+
+
+
+
+
+
+
+
+ +## QUIZ + +What is a suitable `?value` such that + +``` +eval [ "x" := 0, "y" := 12, ...] (x + 1) ==> ?value +``` + +**(A)** `0` + +**(B)** `1` + +**(C)** Error + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +## QUIZ + +What is a suitable `env` such that + +``` +eval env (x + 1) ==> 10 +``` + +**(A)** `[]` + +**(B)** `[x := 0, y := 9]` + +**(C)** `[x := 9, y := 0]` + +**(D)** `[x := 9, y := 10, z := 666]` + +**(E)** `[y := 10, z := 666, x := 9]` + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +## Evaluating Variables + +Using the above intuition, lets update our evaluator +to handle variables i.e. the `EVar` case: + +```haskell +eval env (EVar x) = ??? +``` + +
+
+ +Lets confirm that our `eval` is ok! + +```haskell +envA = [] +envB = ["x" := 0 , "y" := 9] +envC = ["x" := 9 , "y" := 0] +envD = ["x" := 9 , "y" := 10 , "z" := 666] +envE = ["y" := 10, "z" := 666, "x" := 9 ] + +-- >>> eval envA (EBin Add (EVar "x") (ENum 1)) +-- >>> eval envB (EBin Add (EVar "x") (ENum 1)) +-- >>> eval envC (EBin Add (EVar "x") (ENum 1)) +-- >>> eval envD (EBin Add (EVar "x") (ENum 1)) +-- >>> eval envE (EBin Add (EVar "x") (ENum 1)) +``` + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +## The Nano Language + +Features of Nano: + +1. Arithmetic expressions *[done]* +2. Variables *[done]* +3. **Let-bindings** +4. Functions +5. Recursion + +![](/static/img/trinity.png){#fig:types .align-center width=60%} + +
+
+
+
+
+
+
+
+
+ +## 2. Nano: Variables + +Let's add variables and `let` bindings! + +```haskell +e ::= n -- OLD + | e1 + e2 + | e1 - e2 + | e1 * e2 + | x + -- NEW + | let x = e1 in e2 +``` + +Lets extend our datatype + +```haskell +type Id = String + +data Expr + = ENum Int -- OLD + | EBin Binop Expr Expr + | EVar Id + -- NEW + | ELet Id Expr Expr +``` + +How should we extend `eval` ? + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + +## QUIZ + +What *should* the following expression evaluate to? + +```haskell +let x = 0 +in + x + 1 +``` + + +**(A)** Error + +**(B)** `1` + +**(C)** `0` + +
+
+
+
+
+
+
+
+
+ +## QUIZ + +What *should* the following expression evaluate to? + +```haskell +let x = 0 +in + let y = 100 + in + x + y +``` + +**(A)** Error + +**(B)** `0` + +**(C)** `1` + +**(D)** `100` + +**(E)** `101` + +
+
+
+
+
+
+
+
+
+ + + +## QUIZ + +What *should* the following expression evaluate to? + +```haskell +let x = 0 +in + let x = 100 + in + x + 1 +``` + +**(A)** Error + +**(B)** `0` + +**(C)** `1` + +**(D)** `100` + +**(E)** `101` + +
+
+
+
+
+
+
+
+
+ +## QUIZ + +What *should* the following expression evaluate to? + +```haskell +let x = 0 +in + (let x = 100 in + in + x + 1 + ) + + + x +``` + +**(A)** Error + +**(B)** `1` + +**(C)** `101` + +**(D)** `102` + +**(E)** `2` + +
+
+
+
+
+
+
+
+
+ +## Principle: Static/Lexical Scoping + +Every variable *use* gets its value from a unique *definition*: + +- "Nearest" `let`-binder in program *text* + +**Static** means you can tell *without running the program* + +Great for readability and debugging + +1. Define *local* variables + +2. Be sure *where* each variable got its value + +Don’t have to scratch head to figure where a variable got "assigned" + +How to **implement** static scoping? + +
+
+
+
+
+
+
+
+
+
+
+
+ +## QUIZ + +Lets re-evaluate the quizzes! + +```haskell + -- env +let x = 0 +in -- ??? what env to use for `x + 1`? + x + 1 +``` + +**(A)** `env` + +**(B)** `[ ]` + +**(C)** `[ ("x" := 0) ]` + +**(D)** `("x" := 0) : env` + +**(E)** `env ++ ["x" := 0]` + +
+
+
+
+
+
+
+
+
+
+
+
+
+ +## QUIZ + +```haskell + -- env +let x = 0 +in -- (x := 0) : env + let y = 100 + in -- ??? what env to use for `x + y` ? + x + y +``` + +**(A)** `("x" := 0) : env` + +**(B)** `("y" := 100) : env` + +**(C)** `("y" := 100) : ("x" := 0) : env` + +**(D)** `("x" := 0) : ("y" := 100) : env` + +**(E)** `[("y" := 100), ("x" := 0)]` + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +## QUIZ + +Lets re-evaluate the quizzes! + +```haskell + -- env +let x = 0 +in -- ("x" := 0) : env + let x = 100 + in -- ??? what env to use for `x + 1`? + x + 1 +``` + +**(A)** `("x" := 0) : env` + +**(B)** `("x" := 100) : env` + +**(C)** `("x" := 100) : ("x" := 0) : env` + +**(D)** `("x" := 0) : ("x" := 100) : env` + +**(E)** `[("x" := 100)]` + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +## Extending Environments + +Lets fill in `eval` for the `let x = e1 in e2` case! + +```haskell +eval env (ELet x e1 e2) = ??? +``` + +1. **Evaluate** `e1` in `env` to get a value `v1` + +2. **Extend** environment with value for `x` i.e. to `(x := v1) : env` + +3. **Evaluate** `e2` using *extended* environment. + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +Lets make sure our tests pass! + +## Run-time Errors + +Haskell function to *evaluate* an expression: + +```haskell +eval :: Env -> Expr -> Value +eval env (Num n) = n +eval env (Var x) = lookup x env -- (A) +eval env (Bin op e1 e2) = evalOp op v1 v2 -- (B) + where + v1 = eval env e1 -- (C) + v2 = eval env e2 -- (C) +eval env (Let x e1 e2) = eval env1 e2 + where + v1 = eval env e1 + env1 = (x, v1) : env -- (D) +``` + +## QUIZ + +Will `eval env expr` always return a `VInt ...` ? Or, can it `VUndef` ? + +**(A)** operation at `A` may fail +**(B)** operation at `B` may fail +**(C)** operation at `C` may fail +**(D)** operation at `D` may fail +**(E)** nah, its all good..., always returns a `Value` + + +
+
+
+
+
+
+
+
+
+
+ +## Free vs bound variables + +## Undefined Variables + +How do we make sure `lookup` doesn't cause a run-time error? + +**Bound Variables** + +Consider an expression `let x = e1 in e2` + +- An occurrence of `x` is **bound** in `e2` + +- i.e. when occurrence of form `let x = ... in ... x ...` + +- i.e. when `x` occurs "under" a `let` binding for `x`. + +**Free Variables** + +An occurrence of `x` is **free** in `e` if it is **not bound** in `e` + +**Closed Expressions** + +An expression `e` is **closed** in environment `env`: + +- If all **free** variables of `e` are defined in `env` + +**Successful Evaluation** + +`lookup` will never fail + +- If `eval env e` is only called on `e` that is closed in `env` + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +## QUIZ + +Which variables occur free in the expression? + +```haskell +let y = (let x = 2 + in x ) + x +in + let x = 3 + in + x + y +``` + +**(A)** None + +**(B)** `x` + +**(C)** `y` + +**(D)** `x` and `y` + +
+ +(I) final + + *Answer:* B + +
+
+
+
+
+
+
+
+
+ +## Exercise to try at home + +Consider the function + +```haskell +evaluate :: Expr -> Value +evaluate e + | isOk e = eval emptyEnv e + | otherwise = error "Sorry! bad expression, it will crash `eval`!" + where + emptyEnv = [] -- has NO bindings +``` + +What should `isOk` check for? (Try to implement it for `nano`...) + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +## The Nano Language + +Features of Nano: + +1. Arithmetic expressions *[done]* +2. Variables *[done]* +3. Let-bindings *[done]* +4. **Functions** +5. Recursion + +![](/static/img/trinity.png){#fig:types .align-center width=60%} + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +## Nano: Functions + +Let's add + +- *lambda abstraction* (aka function definitions) + +- *application* (aka function calls) + +```haskell +e ::= n -- OLD + | e1 `op` e2 + | x + | let x = e1 in e2 + -- NEW + | \x -> e -- abstraction + | e1 e2 -- application +``` + +### Example + +```haskell +let incr = \x -> x + 1 +in + incr 10 +``` + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + +## Representation + +```haskell +data Expr + = ENum Int -- OLD + | EBin Binop Expr Expr + | EVar Id + | ELet Id Expr Expr + -- NEW + | ??? -- abstraction \x -> e + | ??? -- application (e1 e2) +``` + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + +## Representation + +```haskell +data Expr + = ENum Int -- OLD + | EBin Binop Expr Expr + | EVar Id + | ELet Id Expr Expr + -- NEW + | ELam Id Expr -- abstraction \x -> e + | EApp Expr Expr -- application (e1 e2) +``` + +### Example + +```haskell +let incr = \x -> x + 1 +in + incr 10 +``` + +is represented as + +```haskell +ELet "incr" (ELam "x" (EBin Add (EVar "x") (ENum 1))) + ( + EApp (EVar "incr") (ENum 10) + ) +``` + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + +## Functions are Values + +Recall the trinity + +![](/static/img/trinity.png){#fig:types .align-center width=60%} + +But... what is the _value_ of a function? + +Lets build some intuition with examples. + +
+
+
+
+
+
+
+
+
+
+ +## QUIZ + +What does the following expression evaluate to? + +```haskell +let incr = \x -> x + 1 -- abstraction ("definition") +in + incr 10 -- application ("call") +``` + +**(A)** Error/Undefined + +**(B)** `10` + +**(C)** `11` + +**(D)** `0` + +**(E)** `1` + +
+
+
+
+
+
+
+
+
+
+ +## What is the Value of `incr`? + +- Is it an `Int` ? + +- Is it a `Bool` ? + +- Is it a ??? + +**What information** do we need to store (in the `Env`) about `incr`? + +
+
+
+
+
+
+
+
+
+
+ +## A Function's Value is its Code + +```haskell + -- env +let incr = \x -> x + 1 +in -- ("incr" := ) : env + incr 10 -- evaluate with parameter := 10 +``` + +What information do we store about `` ? + +
+
+
+
+
+
+
+
+
+
+
+ +## A Call's Value + +How to evaluate the "call" `incr 10` ? + +1. Lookup the `` i.e. `` for `incr` (stored in the environment), + +2. Evaluate `body` with `param` set to `10`! + +
+
+
+
+
+
+
+
+
+
+
+
+ +## Two kinds of Values + +We now have _two_ kinds of Values + +```haskell +v ::= n -- OLD + | -- +``` + +1. Plain `Int` (as before) +2. A function's "code": a pair of "parameter" and "body-expression" + +```haskell +data Value + = VInt Int -- OLD + | VCode Id Expr -- +``` + +![](/static/img/trinity.png){#fig:types .align-center width=60%} + +## Evaluating Lambdas and Applications + +```haskell +eval :: Env -> Expr -> Value + -- OLD +eval env (ENum n) = ??? +eval env (EVar x) = ??? +eval env (EBin op e1 e2) = ??? +eval env (ELet x e1 e2) = ??? + -- NEW +eval env (ELam x e) = ??? +eval env (EApp e1 e2) = ??? +``` + +Lets make sure our tests work properly! + +```haskell +exLam1 = ELet "incr" (ELam "x" (EBin Add (EVar "x") (ENum 1))) + ( + EApp (EVar "incr") (ENum 10) + ) + +-- >>> eval [] exLam1 +-- 11 +``` + +## QUIZ + +What should the following evaluate to? + +```haskell +let c = 1 +in + let inc = \x -> x + c + in + inc 10 +``` + +**(A)** Error/Undefined + +**(B)** `10` + +**(C)** `11` + +**(D)** `0` + +**(E)** `1` + +
+
+
+
+
+
+
+
+ +```haskell +exLam2 = ELet "c" (ENum 1) + (ELet "incr" (ELam "x" (EBin Add (EVar "x") (EVar "c"))) + ( + EApp (EVar "incr") (ENum 10) + ) + ) + +-- >>> eval [] exLam2 +-- ??? +``` + +## QUIZ + +And what should _this_ expression evaluate to? + +```haskell +let c = 1 +in + let inc = \x -> x + c + in + let c = 100 + in + inc 10 +``` + +**(A)** Error/Undefined + +**(B)** `110` + +**(C)** `11` + +
+
+
+
+
+
+
+
+ +## The "Immutability Principle" + +A function's behavior should _never change_ + +- A function must _always_ return the same output for a given input + +Why? + +```haskell +> myFunc 10 +0 + +> myFunc 10 +10 +``` + +Oh no! How to find the bug? Is it + +- In `myFunc` or +- In a global variable or +- In a library somewhere else or +- ... + +**My worst debugging nightmare** + +[Colbert "Immutability Principle"](https://youtu.be/CWqzLgDc030?t=628) + + + +
+
+
+
+
+
+
+
+
+
+ +## The Immutability Principle ? + +How does our `eval` work? + +```haskell +exLam3 = ELet "c" (ENum 1) + ( + ELet "incr" (ELam "x" (EBin Add (EVar "x") (EVar "c"))) + ( + ELet "c" (ENum 100) + ( + EApp (EVar "incr") (ENum 10) + ) + ) + ) + +-- >>> eval [] exLam3 +-- ??? +``` + +Oops? + +```haskell + -- [] +let c = 1 +in -- ["c" := 1] + let inc = \x -> x + c + in -- ["inc" := , c := 1] + let c = 100 + in -- ["c" := 100, "inc" := eval ("x" := 10 : env) (x + c) + + ==> 10 + 100 + + ==> 110 +``` + +Ouch. + +
+
+
+
+
+
+
+
+
+
+ +## Enforcing Immutability with Closures + +How to enforce immutability principle + +- `inc 10` **always** returns `11`? + +
+ +### Key Idea: Closures + +**At definition:** +_Freeze_ the environment the function's value + +**At call:** +Use the _frozen_ environment to evaluate the _body_ + +Ensures that `inc 10` _always_ evaluates to the _same_ result! + +```haskell + -- [] +let c = 1 +in -- ["c" := 1] + let inc = \x -> x + c + in -- ["inc" := , c := 1] <<< frozenv = ["c" := 1] + let c = 100 + in -- ["c" := 100, "inc" := , "c" := 1] + inc 10 +``` + +Now we evaluate + +```haskell +eval env (inc 10) + + ==> eval ("x" := 10 : frozenv) (x + c) where frozenv = ["c" := 1] + + ==> 10 + 1 + + ==> 1 +``` + +tada! + +
+
+
+
+
+
+
+
+
+
+ +## Representing Closures + +Lets change the `Value` datatype to also store an `Env` + +```haskell +data Value + = VInt Int -- OLD + | VClos Env Id Expr -- +``` + +
+
+
+
+
+
+
+
+
+
+ +## Evaluating Function Definitions + +How should we fix the definition of `eval` for `ELam`? + +```haskell +eval :: Env -> Expr -> Value + +eval env (ELam x e) = ??? +``` + +**Hint:** What value should we _bind_ `incr` to in our example above? + +
+ +(Recall **At definition** _freeze_ the environment the function's value) + +
+ +## Evaluating Function Calls + +How should we fix the definition of `eval` for `EApp`? + +```haskell +eval :: Env -> Expr -> Value + +eval env (EApp e1 e2) = ??? +``` + +
+ +(Recall **At call:** Use the _frozen_ environment to evaluate the _body_) + +
+ +**Hint:** What value should we _evaluate_ `incr 10` to? + +1. Evaluate `incr` to get `` +2. Evaluate `10` to get `10` +3. Evaluate `x + c` in ` x:=10 : frozenv` + +
+ +Let's generalize that recipe! + +1. Evaluate `e1` to get `` +2. Evaluate `e2` to get `v2` +3. Evaluate `body` in `param := v2 : frozenv` + + +
+
+
+
+
+
+
+
+
+
+ + +## Immutability Achieved + +Lets put our code to the test! + +```haskell +exLam3 = ELet "c" (ENum 1) + ( + ELet "incr" (ELam "x" (EBin Add (EVar "x") (EVar "c"))) + ( + ELet "c" (ENum 100) + ( + EApp (EVar "incr") (ENum 10) + ) + ) + ) + +-- >>> eval [] exLam3 +-- ??? +``` + + +
+
+
+
+
+
+
+
+
+
+ + +## QUIZ + +What *should* the following evaluate to? + +```haskell +let add = \x -> (\y -> x + y) +in + let add10 = add 10 -- \y -> 10 + y + in + let add20 = add 20 -- \y -> 20 + y + in + (add10 100) + (add20 1000) +``` + +**A.** `1100` + +**B.** `1110` + +**C.** `1120` + +**D.** `1130` + +**E.** `1140` + + + +
+
+
+
+
+
+
+
+
+
+ + + +## Functions Returning Functions Achieved! + +```haskell +exLam4 = ... + +-- >>> eval [] exLam4 +``` + + +
+
+
+
+
+
+
+
+
+
+ + + + +## Practice + +What should the following evaluate to? + +```haskell +let add = \x -> (\y -> x + y) +in + let add10 = add 10 + in + let doTwice = \f -> (\x -> f (f x)) + in + doTwice add10 100 +``` + + + +
+
+
+
+
+
+
+
+
+
+ + + +## Functions Accepting Functions Achieved! + +```haskell +exLam5 = ... + +-- >>> eval [] exLam4 +``` + + +
+
+
+
+
+
+
+
+
+
+ + + +## The Nano Language + +![](/static/img/trinity.png){#fig:types .align-center width=60%} + +Features of Nano: + +1. Arithmetic expressions *[done]* +2. Variables *[done]* +3. Let-bindings *[done]* +4. Functions *[done]* +5. **Recursion** + +... You figure it out **Hw4** ... :-) + +
+
+
+
+
+
+
\ No newline at end of file diff --git a/docs/static/code/07-classes.md b/docs/static/code/07-classes.md new file mode 100644 index 0000000..dcefc27 --- /dev/null +++ b/docs/static/code/07-classes.md @@ -0,0 +1,1232 @@ +--- +title: Typeclasses +date: 2019-05-29 +headerImg: books.jpg +--- + +## Past two Weeks + +How to *implement* language constructs? + +- Local variables and scope +- Environments and Closures +- Parsing + +
+
+
+
+
+
+
+
+
+
+
+
+
+ +## Next two Weeks + +Modern language features for structuring programs + +- Overloading +- Type classes +- Monads + +
+
+
+
+
+
+
+
+
+
+
+
+
+ + +## Overloading Operators: Arithmetic + +The `+` operator works for a bunch of different types. + +For `Integer`: + +```haskell +λ> 2 + 3 +5 +``` + +for `Double` precision floats: + +```haskell +λ> 2.9 + 3.5 +6.4 +``` + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +## Overloading Comparisons + +Similarly we can _compare_ different types of values + +```haskell +λ> 2 == 3 +False + +λ> [2.9, 3.5] == [2.9, 3.5] +True + +λ> ("cat", 10) < ("cat", 2) +False + +λ> ("cat", 10) < ("cat", 20) +True +``` + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + +## Ad-Hoc Overloading + +Seems unremarkable? + +Languages since the dawn of time have supported "operator overloading" + +- To support this kind of **ad--hoc polymorphism** + +- Ad-hoc: "created or done for a particular purpose as necessary." + +You really **need** to _add_ and _compare_ values of _multiple_ types! + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + +## Haskell has no caste system + +No distinction between operators and functions + +- All are first class citizens! + +But then, what type do we give to *functions* like `+` and `==` ? + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + +## QUIZ + +Which of the following would be appropriate types for `(+)` ? + +**(A)** `(+) :: Integer -> Integer -> Integer` + +**(B)** `(+) :: Double -> Double -> Double` + +**(C)** `(+) :: a -> a -> a` + +**(D)** _All_ of the above + +**(E)** _None_ of the above + +
+
+
+
+
+
+
+
+
+
+ +`Integer -> Integer -> Integer` is bad because? + +- Then we cannot add `Double`s! + +
+
+
+
+
+
+
+
+
+
+
+
+ +`Double -> Double -> Double` is bad because? + +- Then we cannot add `Double`s! + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + +`a -> a -> a` is bad because? + +- That doesn't make sense, e.g. to add two `Bool` or two `[Int]` or two functions! + +## Type Classes for Ad Hoc Polymorphism + +Haskell solves this problem with an *insanely slick* +mechanism called **type-classes**, introduced by [Wadler and Blott](http://portal.acm.org/citation.cfm?id=75283) + +![](/static/img/blott-wadler.png){#fig:types .align-center width=60%} + +**BTW:** The paper is one of the clearest examples of academic writing I have seen. + +The next time you hear a curmudgeon say all the best CS was done in the 60s, +just point them to the above. + +
+
+
+
+
+
+
+
+
+
+
+
+ +## Qualified Types + +To see the right type, lets ask: + +```haskell +λ> :type (+) +(+) :: (Num a) => a -> a -> a +``` + +We call the above a **qualified type**. Read it as `+` + +- takes in two `a` values and returns an `a` value + +for any type `a` that + +- _is a_ `Num` or +- _implements_ the `Num` interface or +- _is an instance of_ a `Num`. + +The name `Num` can be thought of as a _predicate_ or _constraint_ over types + +- Similar but different than [Java interfaces](https://www.parsonsmatt.org/2017/01/07/how_do_type_classes_differ_from_interfaces.html) + +
+
+
+
+
+
+
+
+
+
+
+
+ + + +## Some types _are_ `Num`s + +Examples include `Integer`, `Double` etc + +- Any such values of those types can be passed to `+`. + +
+
+
+
+
+
+
+
+
+
+
+
+ +## Other types _are not_ `Num`s + +Examples include `Char`, `String`, functions etc, + +- Values of those types _cannot_ be passed to `+`. + +```haskell +λ> True + False + +:15:6: + No instance for (Num Bool) arising from a use of ‘+’ + In the expression: True + False + In an equation for ‘it’: it = True + False +``` + +**Aha!** _Now_ those `no instance for` error messages should make sense! + +- Haskell is complaining that `True` and `False` are of type `Bool` +- and that `Bool` is _not_ an instance of `Num`. + +
+
+
+
+
+
+
+
+
+
+
+
+ + +## Type Class is a Set of Operations + +A type class is a collection of operations (functions) +that must exist for the underlying type. + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + +## The `Eq` Type Class + +The simplest type class is perhaps, `Eq` + +```haskell +class Eq a where + (==) :: a -> a -> Bool + (/=) :: a -> a -> Bool +``` + +A type `a` is _an instance of_ `Eq` if there are two functions + +- `==` and `/=` + +That determine if two `a` values are respectively _equal_ or _disequal_. + +
+
+
+
+
+
+
+
+
+
+
+
+ +## The `Show` Type Class + +The type class `Show` requires that instances be convertible to `String` +(which can then be printed out) + +```haskell +class Show a where + show :: a -> String +``` + +Indeed, we can test this on different (built-in) types + +```haskell +λ> show 2 +"2" + +λ> show 3.14 +"3.14" + +λ> show (1, "two", ([],[],[])) +"(1,\"two\",([],[],[]))" +``` + +(Hey, whats up with the funny `\"`?) + +When we type an expression into ghci, it computes the value +and then calls `show` on the result. Thus, if we create a +*new* type by + +```haskell +data Unshowable = A | B | C +``` + +and then create values of the type, + +```haskell +λ> let x = A +λ> :type x +x :: Unshowable +``` + +but then we **cannot view** them + +```haskell +λ> x + +:1:0: + No instance for (Show Unshowable) + arising from a use of `print' at :1:0 + Possible fix: add an instance declaration for (Show Unshowable) + In a stmt of a 'do' expression: print it +``` + +and we **cannot compare** them! + +```haskell +λ> x == x + +:1:0: + No instance for (Eq Unshowable) + arising from a use of `==' at :1:0-5 + Possible fix: add an instance declaration for (Eq Unshowable) + In the expression: x == x + In the definition of `it': it = x == x +``` + +Again, the previously incomprehensible type error message should +make sense to you. + +
+
+
+
+
+
+
+
+
+
+
+
+ +## Creating Instances + +Tell Haskell how to show or compare values of type `Unshowable` + +By **creating instances** of `Eq` and `Show` for that type: + +```haskell +instance Eq Unshowable where + (==) A A = True -- True if both inputs are A + (==) B B = True -- ...or B + (==) C C = True -- .. or C + (==) _ _ = False -- otherwise + + (/=) x y = not (x == y) -- Test if `x == y` and negate result! +``` + +**EXERCISE** Lets *create* an `instance` for `Show Unshowable` + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +## Automatic Derivation + +This is silly: we _should_ be able to compare and view `Unshowble` "automatically"! + +Haskell lets us _automatically derive_ functions for some classes in the standard library. + +To do so, we simply dress up the data type definition with + +```haskell +data Showable = A' | B' | C' + deriving (Eq, Show) -- tells Haskell to automatically generate instances +``` + +Now we have + +```haskell +λ> let x' = A' + +λ> :type x' +x' :: Showable + +λ> x' +A' + +λ> x' == x' +True + +λ> x' == B' +False +``` + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +## Standard Typeclass Hierarchy + +Let us now peruse the definition of the `Num` typeclass. + +```haskell +λ> :info Num +class (Eq a, Show a) => Num a where + (+) :: a -> a -> a + (*) :: a -> a -> a + (-) :: a -> a -> a + negate :: a -> a + abs :: a -> a + signum :: a -> a + fromInteger :: Integer -> a +``` + +A type `a` _is an instance of_ (i.e. _implements_) `Num` if + +1. The type is *also* an instance of `Eq` and `Show`, and +2. There are functions for adding, multiplying, subtracting, negating + etc values of that type. + +In other words in addition to the "arithmetic" operations, we can +compare two `Num` values and we can view them (as a `String`.) + +Haskell comes equipped with a rich set of built-in classes. + +![Standard Typeclass Hierarchy](/static/img/haskell98-classes.png) + +In the above picture, there is an edge from `Eq` and `Show` to `Num` +because for something to be a `Num` it must also be an `Eq` and `Show`. + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +## The `Ord` Typeclass + +Another typeclass you've used already is the one for `Ord`ering values: + +```haskell +λ> :info (<) +class Eq a => Ord a where + ... + (<) :: a -> a -> Bool + ... +``` + +For example: + +```haskell +λ> 2 < 3 +True + +λ> "cat" < "dog" +True +``` + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +## QUIZ + +Recall the datatype: + +```haskell +data Showable = A' | B' | C' deriving (Eq, Show) +``` + +What is the result of: + +```haskell +λ> A' < B' +``` + +**(A)** `True` +**(B)** `False` +**(C)** Type error +**(D)** Run-time exception + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +## Using Typeclasses + +Typeclasses integrate with the rest of Haskell's type system. + +Lets build a small library for *Environments* mapping keys `k` to values `v` + +```haskell +data Env k v + = Def v -- default value `v` to be used for "missing" keys + | Bind k v (Env k v) -- bind key `k` to the value `v` + deriving (Show) +``` + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +## An API for `Env` + +Lets write a small API for `Env` + +```haskell +-- >>> let env0 = add "cat" 10.0 (add "dog" 20.0 (Def 0)) + +-- >>> get "cat" env0 +-- 10 + +-- >>> get "dog" env0 +-- 20 + +-- >>> get "horse" env0 +-- 0 +``` + +Ok, lets implement! + +```haskell +-- | 'add key val env' returns a new env that additionally maps `key` to `val` +add :: k -> v -> Env k v -> Env k v +add key val env = ??? + +-- | 'get key env' returns the value of `key` and the "default" if no value is found + +get :: k -> Env k v -> v +get key env = ??? +``` + +
+
+
+
+
+
+
+
+
+
+
+
+ +Oops, y u no check? + +## Constraint Propagation + +Lets _delete_ the types of `add` and `get` and see what Haskell says their types are! + +```haskell +λ> :type get +get :: (Eq k) => k -> v -> Env k v -> Env k v +``` + +Haskell tells us that we can use any `k` value as a *key* +as long as the value is an instance of the `Eq` typeclass. + +How, did GHC figure this out? + +- If you look at the code for `get` you'll see that we check if two keys _are equal_! + +
+
+
+
+
+
+
+
+
+
+
+
+
+ +## EXERCISE (Do at home) + +Write an *optimized* version of + +- `add` that ensures the keys are in _increasing_ order, +- `get` that gives up and returns the "default" the moment + we see a key thats larger than the one we're looking for. + +_(How) do you need to change the type of `Env`?_ + +_(How) do you need to change the types of `get` and `add`?_ + +
+
+
+
+
+
+ +## Explicit Signatures + +While Haskell is pretty good about inferring types in general, there are +cases when the use of type classes requires explicit annotations (which +change the behavior of the code.) + +For example, `Read` is a built-in typeclass, where any instance `a` of +`Read` has a function + +```haskell +read :: (Read a) => String -> a +``` + +which can parse a string and turn it into an `a`. + +That is, `Read` is the _opposite_ of `Show`. + +## Quiz + +What does the expression `read "2"` evaluate to? + +**(A)** compile time error + +**(B)** `"2" :: String` + +**(C)** `2 :: Integer` + +**(D)** `2.0 :: Double` + +**(E)** run-time exception + +
+
+
+
+
+
+ +Haskell is foxed! + +- Doesn't know _what type_ to convert the string to! +- Doesn't know _which_ of the `read` functions to run! + +Did we want an `Int` or a `Double` or maybe something else altogether? + +Thus, here an **explicit type annotation** is needed to tell Haskell +what to convert the string to: + +```haskell +λ> (read "2") :: Int +2 + +λ> (read "2") :: Float +2.0 +``` + +Note the different results due to the different types. + +
+
+
+
+
+
+
+
+
+
+
+
+
+ +## Creating Typeclasses + +Typeclasses are useful for *many* different things. + +We will see some of those over the next few lectures. + +Lets conclude today's class with a quick example that provides a small taste. + +
+
+
+
+
+
+
+
+
+
+
+
+
+ +## JSON + +*JavaScript Object Notation* or [JSON](http://www.json.org/) is a simple format for +transferring data around. Here is an example: + +```json +{ "name" : "Ranjit" +, "age" : 41.0 +, "likes" : ["guacamole", "coffee", "bacon"] +, "hates" : [ "waiting" , "grapefruit"] +, "lunches" : [ {"day" : "monday", "loc" : "zanzibar"} + , {"day" : "tuesday", "loc" : "farmers market"} + , {"day" : "wednesday", "loc" : "harekrishna"} + , {"day" : "thursday", "loc" : "faculty club"} + , {"day" : "friday", "loc" : "coffee cart"} ] +} +``` + +In brief, each JSON object is either + +- a *base* value like a string, a number or a boolean, + +- an (ordered) *array* of objects, or + +- a set of *string-object* pairs. + +## A JSON Datatype + +We can represent (a subset of) JSON values with the Haskell datatype + +```haskell +data JVal + = JStr String + | JNum Double + | JBool Bool + | JObj [(String, JVal)] + | JArr [JVal] + deriving (Eq, Ord, Show) +``` + +Thus, the above JSON value would be represented by the `JVal` + +```haskell +js1 = + JObj [("name", JStr "Ranjit") + ,("age", JNum 41.0) + ,("likes", JArr [ JStr "guacamole", JStr "coffee", JStr "bacon"]) + ,("hates", JArr [ JStr "waiting" , JStr "grapefruit"]) + ,("lunches", JArr [ JObj [("day", JStr "monday") + ,("loc", JStr "zanzibar")] + , JObj [("day", JStr "tuesday") + ,("loc", JStr "farmers market")] + , JObj [("day", JStr "wednesday") + ,("loc", JStr "hare krishna")] + , JObj [("day", JStr "thursday") + ,("loc", JStr "faculty club")] + , JObj [("day", JStr "friday") + ,("loc", JStr "coffee cart")] + ]) + ] +``` + +## Serializing Haskell Values to JSON + +Lets write a small library to _serialize_ Haskell values as JSON. + +We could write a bunch of functions like + +```haskell +doubleToJSON :: Double -> JVal +doubleToJSON = JNum + +stringToJSON :: String -> JVal +stringToJSON = JStr + +boolToJSON :: Bool -> JVal +boolToJSON = JBool +``` + +## Serializing Collections + +But what about collections, namely _lists_ of things? + +```haskell +doublesToJSON :: [Double] -> JVal +doublesToJSON xs = JArr (map doubleToJSON xs) + +boolsToJSON :: [Bool] -> JVal +boolsToJSON xs = JArr (map boolToJSON xs) + +stringsToJSON :: [String] -> JVal +stringsToJSON xs = JArr (map stringToJSON xs) +``` + +This is **getting rather tedious** + +- We are rewriting the same code :( + +
+
+
+
+
+
+ +## Serializing Collections (refactored with HOFs) + +You could abstract by making the *individual-element-converter* a parameter + +```haskell +xsToJSON :: (a -> JVal) -> [a] -> JVal +xsToJSON f xs = JArr (map f xs) + +xysToJSON :: (a -> JVal) -> [(String, a)] -> JVal +xysToJSON f kvs = JObj [ (k, f v) | (k, v) <- kvs ] +``` + +But this is *still rather tedious** as you have to pass +in the individual data converter (yuck) + +```haskell +λ> doubleToJSON 4 +JNum 4.0 + +λ> xsToJSON stringToJSON ["coffee", "guacamole", "bacon"] +JArr [JStr "coffee",JStr "guacamole",JStr "bacon"] + +λ> xysToJSON stringToJSON [("day", "monday"), ("loc", "zanzibar")] +JObj [("day",JStr "monday"),("loc",JStr "zanzibar")] +``` + +This gets more hideous when you have richer objects like + +```haskell +lunches = [ [("day", "monday"), ("loc", "zanzibar")] + , [("day", "tuesday"), ("loc", "farmers market")] + ] +``` + +because we have to go through gymnastics like + +```haskell +λ> xsToJSON (xysToJSON stringToJSON) lunches +JArr [ JObj [("day",JStr "monday") ,("loc",JStr "zanzibar")] + , JObj [("day",JStr "tuesday") ,("loc",JStr "farmers market")] + ] +``` + +Yikes. So much for _readability_ + +Is it too much to ask for a magical `toJSON` that _just works?_ + +## Typeclasses To The Rescue + +Lets _define_ a typeclass that describes types `a` that can be converted to JSON. + +```haskell +class JSON a where + toJSON :: a -> JVal +``` + +Now, just make all the above instances of `JSON` like so + +```haskell +instance JSON Double where + toJSON = JNum + +instance JSON Bool where + toJSON = JBool + +instance JSON String where + toJSON = JStr +``` + +This lets us uniformly write + +```haskell +λ> toJSON 4 +JNum 4.0 + +λ> toJSON True +JBool True + +λ> toJSON "guacamole" +JStr "guacamole" +``` + +## Bootstrapping Instances + +The real fun begins when we get Haskell to automatically +bootstrap the above functions to work for lists and key-value lists! + +```haskell +instance JSON a => JSON [a] where + toJSON xs = JArr (map toJSON xs) +``` + +The above says, if `a` is an instance of `JSON`, that is, +if you can convert `a` to `JVal` then here's a generic +recipe to convert lists of `a` values! + +```haskell +λ> toJSON [True, False, True] +JArr [JBln True, JBln False, JBln True] + +λ> toJSON ["cat", "dog", "Mouse"] +JArr [JStr "cat", JStr "dog", JStr "Mouse"] +``` + +or even lists-of-lists! + +```haskell +λ> toJSON [["cat", "dog"], ["mouse", "rabbit"]] +JArr [JArr [JStr "cat",JStr "dog"],JArr [JStr "mouse",JStr "rabbit"]] +``` + +We can pull the same trick with key-value lists + +```haskell +instance (JSON a) => JSON [(String, a)] where + toJSON kvs = JObj [ (k, toJSON v) | (k, v) <- kvs ] +``` + +after which, we are all set! + +```haskell +λ> toJSON lunches +JArr [ JObj [ ("day",JStr "monday"), ("loc",JStr "zanzibar")] + , JObj [("day",JStr "tuesday"), ("loc",JStr "farmers market")] + ] +``` + +It is also useful to bootstrap the serialization for tuples (up to some +fixed size) so we can easily write "non-uniform" JSON objects where keys +are bound to values with different shapes. + +```haskell +instance (JSON a, JSON b) => JSON ((String, a), (String, b)) where + toJSON ((k1, v1), (k2, v2)) = + JObj [(k1, toJSON v1), (k2, toJSON v2)] + +instance (JSON a, JSON b, JSON c) => JSON ((String, a), (String, b), (String, c)) where + toJSON ((k1, v1), (k2, v2), (k3, v3)) = + JObj [(k1, toJSON v1), (k2, toJSON v2), (k3, toJSON v3)] + +instance (JSON a, JSON b, JSON c, JSON d) => JSON ((String, a), (String, b), (String, c), (String,d)) where + toJSON ((k1, v1), (k2, v2), (k3, v3), (k4, v4)) = + JObj [(k1, toJSON v1), (k2, toJSON v2), (k3, toJSON v3), (k4, toJSON v4)] + +instance (JSON a, JSON b, JSON c, JSON d, JSON e) => JSON ((String, a), (String, b), (String, c), (String,d), (String, e)) where + toJSON ((k1, v1), (k2, v2), (k3, v3), (k4, v4), (k5, v5)) = + JObj [(k1, toJSON v1), (k2, toJSON v2), (k3, toJSON v3), (k4, toJSON v4), (k5, toJSON v5)] +``` + +Now, we can simply write + +```haskell +hs = (("name" , "Ranjit") + ,("age" , 41.0) + ,("likes" , ["guacamole", "coffee", "bacon"]) + ,("hates" , ["waiting", "grapefruit"]) + ,("lunches", lunches) + ) +``` + +which is a Haskell value that describes our running JSON example, +and can convert it directly like so + +```haskell +js2 = toJSON hs +``` + +## EXERCISE: Serializing Environments + +To wrap everything up, lets write a routine to serialize our `Env` + +```haskell +instance JSON (Env k v) where + toJSON env = ??? +``` + +and presto! our serializer *just works* + +```haskell +λ> env0 +Bind "cat" 10.0 (Bind "dog" 20.0 (Def 0)) + +λ> toJSON env0 +JObj [ ("cat", JNum 10.0) + , ("dog", JNum 20.0) + , ("def", JNum 0.0) + ] +``` + +Thats it for today. + +We will see much more type class awesomeness in the next few lectures... diff --git a/docs/static/code/08-monads.md b/docs/static/code/08-monads.md new file mode 100644 index 0000000..915762c --- /dev/null +++ b/docs/static/code/08-monads.md @@ -0,0 +1,521 @@ +--- +title: Monads +date: 2019-06-5 +headerImg: books.jpg +--- + +## Abstracting Code Patterns + +a.k.a. **Don't Repeat Yourself** + +### Lists + +```haskell +data List a + = [] + | (:) a (List a) +``` + +
+
+
+ +### Rendering the Values of a List + +```haskell +-- >>> incList [1, 2, 3] +-- ["1", "2", "3"] + +showList :: [Int] -> [String] +showList [] = [] +showList (n:ns) = show n : showList ns +``` + +
+
+
+ +### Squaring the values of a list + +```haskell +-- >>> sqrList [1, 2, 3] +-- 1, 4, 9 + +sqrList :: [Int] -> [Int] +sqrList [] = [] +sqrList (n:ns) = n^2 : sqrList ns +``` + +
+
+
+ +### Common Pattern: `map` over a list + +Refactor iteration into `mapList` + +```haskell +mapList :: (a -> b) -> [a] -> [b] +mapList f [] = [] +mapList f (x:xs) = f x : mapList f xs +``` + +Reuse `map` to implement `inc` and `sqr` + +```haskell +showList xs = map (\n -> show n) xs + +sqrList xs = map (\n -> n ^ 2) xs +``` + +### Trees + +```haskell +data Tree a + = Leaf + | Node a (Tree a) (Tree a) +``` + +
+
+
+ +### Incrementing and Squaring the Values of a Tree + +```haskell +-- >>> showTree (Node 2 (Node 1 Leaf Leaf) (Node 3 Leaf Leaf)) +-- (Node "2" (Node "1" Leaf Leaf) (Node "3" Leaf Leaf)) + +showTree :: Tree Int -> Tree String +showTree Leaf = ??? +showTree (Node v l r) = ??? + +-- >>> sqrTree (Node 2 (Node 1 Leaf Leaf) (Node 3 Leaf Leaf)) +-- (Node 4 (Node 1 Leaf Leaf) (Node 9 Leaf Leaf)) + +sqrTree :: Tree Int -> Tree Int +sqrTree Leaf = ??? +sqrTree (Node v l r) = ??? +``` + +### QUIZ: `map` over a Tree + +Refactor iteration into `mapTree`! What should the type of `mapTree` be? + +```haskell +mapTree :: ??? + +showTree t = mapTree (\n -> show n) t +sqrTree t = mapTree (\n -> n ^ 2) t + +{- A -} (Int -> Int) -> Tree Int -> Tree Int +{- B -} (Int -> String) -> Tree Int -> Tree String +{- C -} (Int -> a) -> Tree Int -> Tree a +{- D -} (a -> a) -> Tree a -> Tree a +{- E -} (a -> b) -> Tree a -> Tree b +``` + +
+
+
+
+
+
+
+
+ +### Lets write `mapTree` + +```haskell +mapTree :: (a -> b) -> Tree a -> Tree b +mapTree f Leaf = ??? +mapTree f (Node v l r) = ??? +``` + +### QUIZ + +Wait ... there is a common pattern across two _datatypes_ + +```haskell +mapList :: (a -> b) -> List a -> List b -- List +mapTree :: (a -> b) -> Tree a -> Tree b -- Tree +``` + +Lets make a `class` for it! + +```haskell +class Mappable t where + map :: ??? +``` + +What type should we give to `map`? + +```haskell +{- A -} (b -> a) -> t b -> t a +{- B -} (a -> a) -> t a -> t a +{- C -} (a -> b) -> [a] -> [b] +{- D -} (a -> b) -> t a -> t b +{- E -} (a -> b) -> Tree a -> Tree b +``` + +
+
+
+
+
+
+ +### Reuse Iteration Across Types + +Haskell's libraries use the name `Functor` instead of `Mappable` + +```haskell +instance Functor [] where + fmap = mapList + +instance Functor Tree where + fmap = mapTree +``` + +And now we can do + +```haskell +-- >>> fmap (\n -> n + 1) (Node 2 (Node 1 Leaf Leaf) (Node 3 Leaf Leaf)) +-- (Node 4 (Node 1 Leaf Leaf) (Node 9 Leaf Leaf)) + +-- >>> fmap show [1,2,3] +-- ["1", "2", "3"] +``` + +### Exercise: Write a `Functor` instance for `Result` + +```haskell +data Result a + = Error String + | Ok a + +instance Functor Result where + fmap f (Error msg) = ??? + fmap f (Ok val) = ??? +``` + +When you're done you should see + +```haskell +-- >>> fmap (\n -> n ^ 2) (Node 2 (Node 1 Leaf Leaf) (Node 3 Leaf Leaf)) +-- (Node 4 (Node 1 Leaf Leaf) (Node 9 Leaf Leaf)) + +-- >>> fmap (\n -> n ^ 2) (Error "oh no") +-- Error "oh no" + +-- >>> fmap (\n -> n ^ 2) (Ok 9) +-- Ok 81 +``` + +## Next: A Class for Sequencing + +Recall our old `Expr` datatype + +```haskell +data Expr + = Number Int + | Plus Expr Expr + | Div Expr Expr + deriving (Show) + +eval :: Expr -> Int +eval (Number n) = n +eval (Plus e1 e2) = eval e1 + eval e2 +eval (Div e1 e2) = eval e1 `div` eval e2 + +-- >>> eval (Div (Number 6) (Number 2)) +-- 3 +``` + +### But, what is the result + +```haskell +-- >>> eval (Div (Number 6) (Number 0)) +-- *** Exception: divide by zero +``` + +A crash! Lets look at an alternative approach to avoid dividing by zero. + +The idea is to return a `Result Int` (instead of a plain `Int`) + +- If a _sub-expression_ had a divide by zero, return `Error "..."` +- If all sub-expressions were safe, then return the actual `Result v` + +```haskell +eval :: Expr -> Result Int +eval (Number n) = Value n +eval (Plus e1 e2) = case e1 of + Error err1 -> Error err1 + Value v1 -> case e2 of + Error err2 -> Error err2 + Value v1 -> Result (v1 + v2) +eval (Div e1 e2) = case e1 of + Error err1 -> Error err1 + Value v1 -> case e2 of + Error err2 -> Error err2 + Value v1 -> if v2 == 0 + then Error ("yikes dbz:" ++ show e2) + else Value (v1 `div` v2) +``` + +The **good news**, no nasty exceptions, just a plain `Error` result + +```haskell +λ> eval (Div (Number 6) (Number 2)) +Value 3 +λ> eval (Div (Number 6) (Number 0)) +Error "yikes dbz:Number 0" +λ> eval (Div (Number 6) (Plus (Number 2) (Number (-2)))) +Error "yikes dbz:Plus (Number 2) (Number (-2))" +``` + +The **bad news**: the code is super duper **gross** + +## Lets spot a Pattern + +The code is gross because we have these cascading blocks + +```haskell +case e1 of + Error err1 -> Error err1 + Value v1 -> case e2 of + Error err2 -> Error err2 + Value v1 -> Result (v1 + v2) +``` + +but really both blocks have something **common pattern** + +```haskell +case e of + Error err -> Error err + Value v -> {- do stuff with v -} +``` + +1. Evaluate `e` +2. If the result is an `Error` then _return_ that error. +3. If the result is a `Value v` then _do further processing_ on `v`. + +Lets **bottle** that common structure in two functions: + +- `>>=` (pronounced _bind_) +- `return` (pronounced _return_) + +![Bottling a Magic Pattern](/static/img/fairy.png){#fig:types .align-center width=20%} + +```haskell +(>>=) :: Result a -> (a -> Result b) -> Result b +(Error err) >>= _ = Error err +(Value v) >>= process = process v + +return :: a -> Result a +return v = Value v +``` + +**NOTE:** `return` is _not_ a keyword; it is just the name of a function! + +## A Cleaned up Evaluator + +The magic bottle lets us clean up our `eval` + +```haskell +eval :: Expr -> Result Int +eval (Number n) = return n +eval (Plus e1 e2) = eval e1 >>= \v1 -> + eval e2 >>= \v2 -> + return (v1 + v2) +eval (Div e1 e2) = eval e1 >>= \v1 -> + eval e2 >>= \v2 -> + if v2 == 0 + then Error ("yikes dbz:" ++ show e2) + else return (v1 `div` v2) +``` + +**The gross _pattern matching_ is all hidden inside `>>=`** + +Notice the `>>=` takes *two* inputs of type: + +- `Result Int` (e.g. `eval e1` or `eval e2`) +- `Int -> Result Int` (e.g. The _processing_ function that takes the `v` and does stuff with it) + +In the above, the processing functions are written using `\v1 -> ...` and `\v2 -> ...` + +**NOTE:** It is _crucial_ that you understand what the code above +is doing, and why it is actually just a "shorter" version of the +(gross) nested-case-of `eval`. + +## A Class for `>>=` + +Like `fmap` or `show` or `jval` or `==`, or `<=`, +the `>>=` operator is useful across **many** types! + +Lets capture it in an interface/typeclass: + +```haskell +class Monad m where + (>>=) :: m a -> (a -> m b) -> m b + return :: a -> m a +``` + +Notice how the definitions for `Result` fit the above, with `m = Result` + +```haskell +instance Monad Result where + (>>=) :: Either a -> (a -> Either b) -> Either b + (Error err) >>= _ = Error err + (Value v) >>= process = process v + + return :: a -> Result a + return v = Value v +``` + +## Syntax for `>>=` + +In fact `>>=` is *so* useful there is special syntax for it. + +Instead of writing + +```haskell +e1 >>= \v1 -> + e2 >>= \v2 -> + e3 >>= \v3 -> + e +``` + +you can write + +```haskell +do v1 <- e1 + v2 <- e2 + v3 <- e3 + e +... +``` + +Thus, we can further simplify our `eval` to: + +```haskell +eval :: Expr -> Result Int +eval (Number n) = return n +eval (Plus e1 e2) = do v1 <- eval e1 + v2 <- eval e2 + return (v1 + v2) +eval (Div e1 e2) = do v1 <- eval e1 + v2 <- eval e2 + if v2 == 0 + then Error ("yikes dbz:" ++ show e2) + else return (v1 `div` v2) +``` + +
+
+
+
+
+
+
+
+ +## Generalizing `Result` to *Many* Values + +We can generalize `Result` to "many" values, using `List` + +```haskell +data Result a = Err String | Result a +data List a = Nil | Cons a (List a) +``` + +- The `Err` is like `[]` except it has a message too, +- The `tail` of type `(List a)` lets us hold *many* possible `a` values + +We can now make a `Monad` instance for lists as + +```haskell +instance Monad [] where + return = returnList + (>>=) = bindList + +returnList :: a -> [a] +returnList x = [x] + +bindList :: [a] -> (a -> [b]) -> [b] +bindList [] f = [] +bindList (x:xs) f = f x ++ bindList xs f +``` + +Notice `bindList xs f` is like a `for-loop`: + +- **for each** `x` in `xs` we call, +- `f x` to get the results +- and concatenate _all_ the results + +so, + +```haskell +bindList [x1,x2,x3,...,xn] f ==> + f x1 ++ f x2 ++ f x3 ++ ... ++ f xn +``` + +This has some fun consequences! + +```haskell +silly xs = do + x <- xs + return (x * 10) +``` + +produces the result + +```haskell +-- >>> silly [1,2,3] +-- [10,20,30] +``` + +and + +```haskell +foo xs ys = do + x <- xs + y <- ys + return (x, y) +``` + +produces the result + +```haskell +-- >>> foo ["1", "2", "red", "blue"] ["fish"] +-- [("1","fish"),("2","fish"),("red","fish"),("blue","fish")] +``` + +behaves like Python's + +```python +for x in xs: + for y in ys: + yield (x, y) +``` + +## EXERCISE + +Fill in the blanks to implement `mMap` (i.e. `map` using monads) + +```haskell +mMap :: (a -> b) -> [a] -> [b] +mMap f xs = do + _fixme +``` + +## EXERCISE + +Fill in the blanks to implement `mFilter` (i.e. `filter` using monads) + +```haskell +mFilter :: (a -> Bool) -> [a] -> [a] +mFilter f xs = do + _fixme +``` + diff --git a/docs/static/code/09-io.md b/docs/static/code/09-io.md new file mode 100644 index 0000000..1aaf867 --- /dev/null +++ b/docs/static/code/09-io.md @@ -0,0 +1,570 @@ +--- +title: Hello, world! (The IO Monad) +date: 2019-06-5 +headerImg: books.jpg +--- + +## Writing Applications + +In most language related classes, we _start_ with a "Hello world!" program. + +With 130, we will _end_ with it. + +
+
+
+
+
+
+
+
+
+
+
+ + + + + +## Purity and the Immutability Principle + +Haskell is a **pure** language. Not a _value_ judgment, but a precise _technical_ statement: + +**The "Immutability Principle":** + +- A function must _always_ return the same output for a given input + +- A function's behavior should _never change_ + +
+
+
+
+
+
+
+
+ +## No Side Effects + +![](/static/img/trinity.png){#fig:types .align-center width=60%} + +Haskell's most radical idea: `expression ==> value` + +- When you evaluate an expression you get a value and **nothing else happens** + +Specifically, evaluation must not have an **side effects** + +- _change_ a global variable or + +- _print_ to screen or + +- _read_ a file or + +- _send_ an email or + +- _launch_ a missile. + +
+
+
+
+
+
+
+
+ + +## Purity + +Means _functions may depend only on their inputs_ + +- i.e. **functions should give the same output for the same input every time.** + +
+
+
+
+
+
+
+
+ + +## But... how to write "Hello, world!" + +But, we _want_ to ... + +- print to screen +- read a file +- send an email + +A language that only lets you write `factorial` and `fibonacci` is ... _not very useful_! + +Thankfully, you _can_ do all the above via a very clever idea: `Recipe` + +
+
+
+
+
+
+
+
+ +## Recipes + +[This analogy is due to Joachim Brietner][brietner] + +Haskell has a special type called `IO` -- which you can think of as `Recipe` + +```haskell +type Recipe a = IO a +``` + +A _value_ of type `Recipe a` is + +- a **description** of an effectful computations + +- when **when executed** (possibly) perform some effectful I/O operations to + +- **produce** a value of type `a`. + +
+
+
+
+
+
+
+
+
+
+
+ + + + +## Recipes have No Effects + +A value of type `Recipe a` is + +- Just a **description** of an effectful computation + +- An inert, perfectly safe thing with **no effects**. + +![Cake vs. Recipe](/static/img/cake.png){#fig:types .align-center width=80%} + +**(L)** chocolate _cake_, **(R)** a _sequence of instructions_ on how to make a cake. + +They are different (_hint_: only one of them is delicious.) + +Merely having a `Recipe Cake` has no effects: holding the recipe + +- Does not make your oven _hot_ + +- Does not make your your floor _dirty_ + +
+
+
+
+
+
+
+
+
+
+
+ + + +## Executing Recipes + +There is **only one way** to execute a `Recipe a` + +Haskell looks for a special value + +```haskell +main :: Recipe () +``` + +The value associated with `main` is handed to the **runtime system and executed** + +![Baker Aker](/static/img/baker-aker.jpg){#fig:types .align-center width=70%} + +The Haskell runtime is a _master chef_ who is the only one allowed to cook! + +
+
+
+
+
+
+
+
+
+
+
+ + + + +## How to write an App in Haskell + +Make a `Recipe ()` that is handed off to the master chef `main`. + +- `main` can be arbitrarily complicated + +- will be composed of _many smaller_ recipes + +
+
+
+
+
+
+
+
+
+
+
+ + +## Hello World + + +```haskell +putStrLn :: String -> Recipe () +``` + +The function `putStrLn` + +- takes as input a `String` +- returns as output a `Recipe ()` + +`putStrLn msg` is a `Recipe ()` _when executed_ prints out `msg` on the screen. + +```haskell +main :: Recipe () +main = putStrLn "Hello, world!" +``` + +... and we can compile and run it + +```sh +$ ghc --make hello.hs +$ ./hello +Hello, world! +``` + +
+
+
+
+
+
+
+
+
+
+
+ + +## QUIZ: Combining Recipes + +Next, lets write a program that prints multiple things: + +```haskell +main :: IO () +main = combine (putStrLn "Hello,") (putStrLn "World!") + +-- putStrLn :: String -> Recipe () +-- combine :: ??? +``` + +What must the _type_ of `combine` be? + +```haskell +{- A -} combine :: () -> () -> () +{- B -} combine :: Recipe () -> Recipe () -> Recipe () +{- C -} combine :: Recipe a -> Recipe a -> Recipe a +{- D -} combine :: Recipe a -> Recipe b -> Recipe b +{- E -} combine :: Recipe a -> Recipe b -> Recipe a +``` + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + +## Using Intermediate Results + +Next, lets write a program that + +1. **Asks** for the user's `name` using + +```haskell + getLine :: Recipe String +``` + +2. **Prints** out a greeting with that `name` using + +```haskell + putStrLn :: String -> Recipe () +``` + +**Problem:** How to pass the **output** of _first_ recipe into the _second_ recipe? + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + +## QUIZ: Using Yolks to Make Batter + +Suppose you have two recipes + +```haskell +crack :: Recipe Yolk +eggBatter :: Yolk -> Recipe Batter +``` + +and we want to get + +```haskell +mkBatter :: Recipe Batter +mkBatter = crack `combineWithResult` eggBatter +``` + +What must the type of `combineWithResult` be? + +```haskell +{- A -} Yolk -> Batter -> Batter +{- B -} Recipe Yolk -> (Yolk -> Recipe Batter) -> Recipe Batter +{- C -} Recipe a -> (a -> Recipe a ) -> Recipe a +{- D -} Recipe a -> (a -> Recipe b ) -> Recipe b +{- E -} Recipe Yolk -> (Yolk -> Recipe Batter) -> Recipe () +``` + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + +## Looks Familiar + +Wait a bit, the signature looks familiar! + +```haskell +combineWithResult :: Recipe a -> (a -> Recipe b) -> Recipe b +``` + +Remember this + +```haskell +(>>=) :: Result a -> (a -> Result b) -> Result b +``` + +
+
+
+
+
+
+
+
+
+
+
+ + +## `Recipe` is an instance of `Monad` + +In fact, in the standard library + +```haskell +instance Monad Recipe where + (>>=) = {-... combineWithResult... -} +``` + +So we can put this together with `putStrLn` to get: + +```haskell +main :: Recipe () +main = getLine >>= \name -> putStrLn ("Hello, " ++ name ++ "!") +``` + +or, using `do` notation the above becomes + +```haskell +main :: Recipe () +main = do name <- getLine + putStrLn ("Hello, " ++ name ++ "!") +``` + +
+
+
+
+
+
+
+
+
+
+
+ + +## EXERCISE + +1. _Compile_ and run to make sure its ok! + +2. _Modify_ the above to repeatedly ask for names. + +3. _Extend_ the above to print a "prompt" that tells you how many iterations have occurred. + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + +## Monads are Amazing + +Monads have had a _revolutionary_ influence in PL, well beyond Haskell, some recent examples + +- **Error handling** in `go` e.g. [1](https://speakerdeck.com/rebeccaskinner/monadic-error-handling-in-go) and [2](https://www.innoq.com/en/blog/golang-errors-monads/) + +- **Asynchrony** in JavaScript e.g. [1](https://gist.github.com/MaiaVictor/bc0c02b6d1fbc7e3dbae838fb1376c80) and [2](https://medium.com/@dtipson/building-a-better-promise-3dd366f80c16) + +- **Big data** pipelines e.g. [LinQ](https://www.microsoft.com/en-us/research/project/dryadlinq/) and [TensorFlow](https://www.tensorflow.org/) + +
+
+
+
+
+
+
+ +## A Silly App to End CSE 130 + +Lets write an app called [moo](/static/raw/moo.hs) inspired by [cowsay](https://medium.com/@jasonrigden/cowsay-is-the-most-important-unix-like-command-ever-35abdbc22b7f) + +**A Command Line App** + +![`moo`](/static/img/moo1.png){#fig:types .align-center width=70%} + +**`moo` works with pipes** + +![Thanks, and good luck for the final!](/static/img/moo2.png){#fig:types .align-center width=70%} + + + + +[brietner]: https://www.seas.upenn.edu/~cis194/fall16/lectures/06-io-and-monads.html \ No newline at end of file diff --git a/docs/static/code/README.md b/docs/static/code/README.md new file mode 100644 index 0000000..ea77158 --- /dev/null +++ b/docs/static/code/README.md @@ -0,0 +1,4 @@ +## README + + +Code files for CSE 230 diff --git a/docs/static/code/cse130-code.cabal b/docs/static/code/cse130-code.cabal new file mode 100644 index 0000000..c01b9bc --- /dev/null +++ b/docs/static/code/cse130-code.cabal @@ -0,0 +1,41 @@ +cabal-version: 1.12 + +-- This file has been generated from package.yaml by hpack version 0.33.0. +-- +-- see: https://github.com/sol/hpack +-- +-- hash: ae32a1be1aae20589c6fe444c2a63e4b62a69ccf82ac749602b0688bee9681b4 + +name: cse130-code +version: 0.1.0.0 +homepage: https://github.com/ucsd-cse130/wi21#readme +bug-reports: https://github.com/ucsd-cse130/wi21/issues +author: Ranjit Jhala +maintainer: jhala@cs.ucsd.edu +copyright: 2020 Ranjit Jhala +license: MIT +build-type: Simple +extra-source-files: + README.md + +source-repository head + type: git + location: https://github.com/ucsd-cse130/wi21 + +library + other-modules: + Paths_cse130_code + ghc-options: -Wall -fwarn-tabs + + hs-source-dirs: + src + build-depends: + QuickCheck + , base >=4.7 && <5 + , containers + , directory + , filepath + , mtl + , process + , unordered-containers + default-language: Haskell2010 diff --git a/docs/static/code/foo b/docs/static/code/foo new file mode 100644 index 0000000..416f4ec --- /dev/null +++ b/docs/static/code/foo @@ -0,0 +1,8 @@ + + + +n = \n f x -> f (f (f (f (f x)))) + +REPEAT 3 x = (CONS x (CONS x (CONS x NIL))) + +REPEAT = \n x -> n (CONS x) NIL diff --git a/docs/static/code/hie.yaml b/docs/static/code/hie.yaml new file mode 100644 index 0000000..46b71df --- /dev/null +++ b/docs/static/code/hie.yaml @@ -0,0 +1,4 @@ +cradle: + cabal: + - path: "src" + component: "lib:cse130-code" diff --git a/docs/static/code/hie.yaml.stack b/docs/static/code/hie.yaml.stack new file mode 100644 index 0000000..1f1c730 --- /dev/null +++ b/docs/static/code/hie.yaml.stack @@ -0,0 +1,4 @@ +cradle: + stack: + - path: "./src" + component: "cse130-code:lib" diff --git a/docs/static/code/package.yaml b/docs/static/code/package.yaml new file mode 100644 index 0000000..ac1c94b --- /dev/null +++ b/docs/static/code/package.yaml @@ -0,0 +1,23 @@ +name: cse130-code +version: 0.1.0.0 +github: "ucsd-cse130/wi21" +license: MIT +author: "Ranjit Jhala" +maintainer: "jhala@cs.ucsd.edu" +copyright: "2022 Ranjit Jhala" + +extra-source-files: +- README.md + +dependencies: +- base >= 4.7 && < 5 +- process +- directory +- filepath +- containers +- unordered-containers +- QuickCheck +- mtl + +library: + source-dirs: src diff --git a/docs/static/code/package.yaml.old b/docs/static/code/package.yaml.old new file mode 100644 index 0000000..ac1c94b --- /dev/null +++ b/docs/static/code/package.yaml.old @@ -0,0 +1,23 @@ +name: cse130-code +version: 0.1.0.0 +github: "ucsd-cse130/wi21" +license: MIT +author: "Ranjit Jhala" +maintainer: "jhala@cs.ucsd.edu" +copyright: "2022 Ranjit Jhala" + +extra-source-files: +- README.md + +dependencies: +- base >= 4.7 && < 5 +- process +- directory +- filepath +- containers +- unordered-containers +- QuickCheck +- mtl + +library: + source-dirs: src diff --git a/docs/static/code/stack.yaml b/docs/static/code/stack.yaml new file mode 100644 index 0000000..ebfcdd4 --- /dev/null +++ b/docs/static/code/stack.yaml @@ -0,0 +1,6 @@ +resolver: lts-18.13 +compiler: ghc-8.10.7 +allow-newer: true +packages: +- '.' + diff --git a/docs/static/code/stack.yaml.lock b/docs/static/code/stack.yaml.lock new file mode 100644 index 0000000..14fcf76 --- /dev/null +++ b/docs/static/code/stack.yaml.lock @@ -0,0 +1,12 @@ +# This file was autogenerated by Stack. +# You should not edit this file by hand. +# For more information, please see the documentation at: +# https://docs.haskellstack.org/en/stable/lock_files + +packages: [] +snapshots: +- completed: + size: 586268 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/13.yaml + sha256: d9e658a22cfe8d87a64fdf219885f942fef5fe2bcb156a9800174911c5da2443 + original: lts-18.13 diff --git a/docs/static/css/bootstrap.css b/docs/static/css/bootstrap.css new file mode 100644 index 0000000..c6f3d21 --- /dev/null +++ b/docs/static/css/bootstrap.css @@ -0,0 +1,6332 @@ +/*! + * Bootstrap v3.3.1 (http://getbootstrap.com) + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ + +/*! normalize.css v3.0.2 | MIT License | git.io/normalize */ +html { + font-family: sans-serif; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} +body { + margin: 0; +} +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +menu, +nav, +section, +summary { + display: block; +} +audio, +canvas, +progress, +video { + display: inline-block; + vertical-align: baseline; +} +audio:not([controls]) { + display: none; + height: 0; +} +[hidden], +template { + display: none; +} +a { + background-color: transparent; +} +a:active, +a:hover { + outline: 0; +} +abbr[title] { + border-bottom: 1px dotted; +} +b, +strong { + font-weight: bold; +} +dfn { + font-style: italic; +} +h1 { + margin: .67em 0; + font-size: 2em; +} +mark { + color: #000; + background: #ff0; +} +small { + font-size: 80%; +} +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} +sup { + top: -.5em; +} +sub { + bottom: -.25em; +} +img { + border: 0; +} +svg:not(:root) { + overflow: hidden; +} +figure { + margin: 1em 40px; +} +hr { + height: 0; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} +pre { + overflow: auto; +} +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} +button, +input, +optgroup, +select, +textarea { + margin: 0; + font: inherit; + color: inherit; +} +button { + overflow: visible; +} +button, +select { + text-transform: none; +} +button, +html input[type="button"], +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; + cursor: pointer; +} +button[disabled], +html input[disabled] { + cursor: default; +} +button::-moz-focus-inner, +input::-moz-focus-inner { + padding: 0; + border: 0; +} +input { + line-height: normal; +} +input[type="checkbox"], +input[type="radio"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 0; +} +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} +input[type="search"] { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + -webkit-appearance: textfield; +} +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} +fieldset { + padding: .35em .625em .75em; + margin: 0 2px; + border: 1px solid #c0c0c0; +} +legend { + padding: 0; + border: 0; +} +textarea { + overflow: auto; +} +optgroup { + font-weight: bold; +} +table { + border-spacing: 0; + border-collapse: collapse; +} +td, +th { + padding: 0; +} +/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ +@media print { + *, + *:before, + *:after { + color: #000 !important; + text-shadow: none !important; + background: transparent !important; + -webkit-box-shadow: none !important; + box-shadow: none !important; + } + a, + a:visited { + text-decoration: underline; + } + a[href]:after { + content: " (" attr(href) ")"; + } + abbr[title]:after { + content: " (" attr(title) ")"; + } + a[href^="#"]:after, + a[href^="javascript:"]:after { + content: ""; + } + pre, + blockquote { + border: 1px solid #999; + + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + img { + max-width: 100% !important; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } + select { + background: #fff !important; + } + .navbar { + display: none; + } + .btn > .caret, + .dropup > .btn > .caret { + border-top-color: #000 !important; + } + .label { + border: 1px solid #000; + } + .table { + border-collapse: collapse !important; + } + .table td, + .table th { + background-color: #fff !important; + } + .table-bordered th, + .table-bordered td { + border: 1px solid #ddd !important; + } +} +@font-face { + font-family: 'Glyphicons Halflings'; + + src: url('../fonts/glyphicons-halflings-regular.eot'); + src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); +} +.glyphicon { + position: relative; + top: 1px; + display: inline-block; + font-family: 'Glyphicons Halflings'; + font-style: normal; + font-weight: normal; + line-height: 1; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.glyphicon-asterisk:before { + content: "\2a"; +} +.glyphicon-plus:before { + content: "\2b"; +} +.glyphicon-euro:before, +.glyphicon-eur:before { + content: "\20ac"; +} +.glyphicon-minus:before { + content: "\2212"; +} +.glyphicon-cloud:before { + content: "\2601"; +} +.glyphicon-envelope:before { + content: "\2709"; +} +.glyphicon-pencil:before { + content: "\270f"; +} +.glyphicon-glass:before { + content: "\e001"; +} +.glyphicon-music:before { + content: "\e002"; +} +.glyphicon-search:before { + content: "\e003"; +} +.glyphicon-heart:before { + content: "\e005"; +} +.glyphicon-star:before { + content: "\e006"; +} +.glyphicon-star-empty:before { + content: "\e007"; +} +.glyphicon-user:before { + content: "\e008"; +} +.glyphicon-film:before { + content: "\e009"; +} +.glyphicon-th-large:before { + content: "\e010"; +} +.glyphicon-th:before { + content: "\e011"; +} +.glyphicon-th-list:before { + content: "\e012"; +} +.glyphicon-ok:before { + content: "\e013"; +} +.glyphicon-remove:before { + content: "\e014"; +} +.glyphicon-zoom-in:before { + content: "\e015"; +} +.glyphicon-zoom-out:before { + content: "\e016"; +} +.glyphicon-off:before { + content: "\e017"; +} +.glyphicon-signal:before { + content: "\e018"; +} +.glyphicon-cog:before { + content: "\e019"; +} +.glyphicon-trash:before { + content: "\e020"; +} +.glyphicon-home:before { + content: "\e021"; +} +.glyphicon-file:before { + content: "\e022"; +} +.glyphicon-time:before { + content: "\e023"; +} +.glyphicon-road:before { + content: "\e024"; +} +.glyphicon-download-alt:before { + content: "\e025"; +} +.glyphicon-download:before { + content: "\e026"; +} +.glyphicon-upload:before { + content: "\e027"; +} +.glyphicon-inbox:before { + content: "\e028"; +} +.glyphicon-play-circle:before { + content: "\e029"; +} +.glyphicon-repeat:before { + content: "\e030"; +} +.glyphicon-refresh:before { + content: "\e031"; +} +.glyphicon-list-alt:before { + content: "\e032"; +} +.glyphicon-lock:before { + content: "\e033"; +} +.glyphicon-flag:before { + content: "\e034"; +} +.glyphicon-headphones:before { + content: "\e035"; +} +.glyphicon-volume-off:before { + content: "\e036"; +} +.glyphicon-volume-down:before { + content: "\e037"; +} +.glyphicon-volume-up:before { + content: "\e038"; +} +.glyphicon-qrcode:before { + content: "\e039"; +} +.glyphicon-barcode:before { + content: "\e040"; +} +.glyphicon-tag:before { + content: "\e041"; +} +.glyphicon-tags:before { + content: "\e042"; +} +.glyphicon-book:before { + content: "\e043"; +} +.glyphicon-bookmark:before { + content: "\e044"; +} +.glyphicon-print:before { + content: "\e045"; +} +.glyphicon-camera:before { + content: "\e046"; +} +.glyphicon-font:before { + content: "\e047"; +} +.glyphicon-bold:before { + content: "\e048"; +} +.glyphicon-italic:before { + content: "\e049"; +} +.glyphicon-text-height:before { + content: "\e050"; +} +.glyphicon-text-width:before { + content: "\e051"; +} +.glyphicon-align-left:before { + content: "\e052"; +} +.glyphicon-align-center:before { + content: "\e053"; +} +.glyphicon-align-right:before { + content: "\e054"; +} +.glyphicon-align-justify:before { + content: "\e055"; +} +.glyphicon-list:before { + content: "\e056"; +} +.glyphicon-indent-left:before { + content: "\e057"; +} +.glyphicon-indent-right:before { + content: "\e058"; +} +.glyphicon-facetime-video:before { + content: "\e059"; +} +.glyphicon-picture:before { + content: "\e060"; +} +.glyphicon-map-marker:before { + content: "\e062"; +} +.glyphicon-adjust:before { + content: "\e063"; +} +.glyphicon-tint:before { + content: "\e064"; +} +.glyphicon-edit:before { + content: "\e065"; +} +.glyphicon-share:before { + content: "\e066"; +} +.glyphicon-check:before { + content: "\e067"; +} +.glyphicon-move:before { + content: "\e068"; +} +.glyphicon-step-backward:before { + content: "\e069"; +} +.glyphicon-fast-backward:before { + content: "\e070"; +} +.glyphicon-backward:before { + content: "\e071"; +} +.glyphicon-play:before { + content: "\e072"; +} +.glyphicon-pause:before { + content: "\e073"; +} +.glyphicon-stop:before { + content: "\e074"; +} +.glyphicon-forward:before { + content: "\e075"; +} +.glyphicon-fast-forward:before { + content: "\e076"; +} +.glyphicon-step-forward:before { + content: "\e077"; +} +.glyphicon-eject:before { + content: "\e078"; +} +.glyphicon-chevron-left:before { + content: "\e079"; +} +.glyphicon-chevron-right:before { + content: "\e080"; +} +.glyphicon-plus-sign:before { + content: "\e081"; +} +.glyphicon-minus-sign:before { + content: "\e082"; +} +.glyphicon-remove-sign:before { + content: "\e083"; +} +.glyphicon-ok-sign:before { + content: "\e084"; +} +.glyphicon-question-sign:before { + content: "\e085"; +} +.glyphicon-info-sign:before { + content: "\e086"; +} +.glyphicon-screenshot:before { + content: "\e087"; +} +.glyphicon-remove-circle:before { + content: "\e088"; +} +.glyphicon-ok-circle:before { + content: "\e089"; +} +.glyphicon-ban-circle:before { + content: "\e090"; +} +.glyphicon-arrow-left:before { + content: "\e091"; +} +.glyphicon-arrow-right:before { + content: "\e092"; +} +.glyphicon-arrow-up:before { + content: "\e093"; +} +.glyphicon-arrow-down:before { + content: "\e094"; +} +.glyphicon-share-alt:before { + content: "\e095"; +} +.glyphicon-resize-full:before { + content: "\e096"; +} +.glyphicon-resize-small:before { + content: "\e097"; +} +.glyphicon-exclamation-sign:before { + content: "\e101"; +} +.glyphicon-gift:before { + content: "\e102"; +} +.glyphicon-leaf:before { + content: "\e103"; +} +.glyphicon-fire:before { + content: "\e104"; +} +.glyphicon-eye-open:before { + content: "\e105"; +} +.glyphicon-eye-close:before { + content: "\e106"; +} +.glyphicon-warning-sign:before { + content: "\e107"; +} +.glyphicon-plane:before { + content: "\e108"; +} +.glyphicon-calendar:before { + content: "\e109"; +} +.glyphicon-random:before { + content: "\e110"; +} +.glyphicon-comment:before { + content: "\e111"; +} +.glyphicon-magnet:before { + content: "\e112"; +} +.glyphicon-chevron-up:before { + content: "\e113"; +} +.glyphicon-chevron-down:before { + content: "\e114"; +} +.glyphicon-retweet:before { + content: "\e115"; +} +.glyphicon-shopping-cart:before { + content: "\e116"; +} +.glyphicon-folder-close:before { + content: "\e117"; +} +.glyphicon-folder-open:before { + content: "\e118"; +} +.glyphicon-resize-vertical:before { + content: "\e119"; +} +.glyphicon-resize-horizontal:before { + content: "\e120"; +} +.glyphicon-hdd:before { + content: "\e121"; +} +.glyphicon-bullhorn:before { + content: "\e122"; +} +.glyphicon-bell:before { + content: "\e123"; +} +.glyphicon-certificate:before { + content: "\e124"; +} +.glyphicon-thumbs-up:before { + content: "\e125"; +} +.glyphicon-thumbs-down:before { + content: "\e126"; +} +.glyphicon-hand-right:before { + content: "\e127"; +} +.glyphicon-hand-left:before { + content: "\e128"; +} +.glyphicon-hand-up:before { + content: "\e129"; +} +.glyphicon-hand-down:before { + content: "\e130"; +} +.glyphicon-circle-arrow-right:before { + content: "\e131"; +} +.glyphicon-circle-arrow-left:before { + content: "\e132"; +} +.glyphicon-circle-arrow-up:before { + content: "\e133"; +} +.glyphicon-circle-arrow-down:before { + content: "\e134"; +} +.glyphicon-globe:before { + content: "\e135"; +} +.glyphicon-wrench:before { + content: "\e136"; +} +.glyphicon-tasks:before { + content: "\e137"; +} +.glyphicon-filter:before { + content: "\e138"; +} +.glyphicon-briefcase:before { + content: "\e139"; +} +.glyphicon-fullscreen:before { + content: "\e140"; +} +.glyphicon-dashboard:before { + content: "\e141"; +} +.glyphicon-paperclip:before { + content: "\e142"; +} +.glyphicon-heart-empty:before { + content: "\e143"; +} +.glyphicon-link:before { + content: "\e144"; +} +.glyphicon-phone:before { + content: "\e145"; +} +.glyphicon-pushpin:before { + content: "\e146"; +} +.glyphicon-usd:before { + content: "\e148"; +} +.glyphicon-gbp:before { + content: "\e149"; +} +.glyphicon-sort:before { + content: "\e150"; +} +.glyphicon-sort-by-alphabet:before { + content: "\e151"; +} +.glyphicon-sort-by-alphabet-alt:before { + content: "\e152"; +} +.glyphicon-sort-by-order:before { + content: "\e153"; +} +.glyphicon-sort-by-order-alt:before { + content: "\e154"; +} +.glyphicon-sort-by-attributes:before { + content: "\e155"; +} +.glyphicon-sort-by-attributes-alt:before { + content: "\e156"; +} +.glyphicon-unchecked:before { + content: "\e157"; +} +.glyphicon-expand:before { + content: "\e158"; +} +.glyphicon-collapse-down:before { + content: "\e159"; +} +.glyphicon-collapse-up:before { + content: "\e160"; +} +.glyphicon-log-in:before { + content: "\e161"; +} +.glyphicon-flash:before { + content: "\e162"; +} +.glyphicon-log-out:before { + content: "\e163"; +} +.glyphicon-new-window:before { + content: "\e164"; +} +.glyphicon-record:before { + content: "\e165"; +} +.glyphicon-save:before { + content: "\e166"; +} +.glyphicon-open:before { + content: "\e167"; +} +.glyphicon-saved:before { + content: "\e168"; +} +.glyphicon-import:before { + content: "\e169"; +} +.glyphicon-export:before { + content: "\e170"; +} +.glyphicon-send:before { + content: "\e171"; +} +.glyphicon-floppy-disk:before { + content: "\e172"; +} +.glyphicon-floppy-saved:before { + content: "\e173"; +} +.glyphicon-floppy-remove:before { + content: "\e174"; +} +.glyphicon-floppy-save:before { + content: "\e175"; +} +.glyphicon-floppy-open:before { + content: "\e176"; +} +.glyphicon-credit-card:before { + content: "\e177"; +} +.glyphicon-transfer:before { + content: "\e178"; +} +.glyphicon-cutlery:before { + content: "\e179"; +} +.glyphicon-header:before { + content: "\e180"; +} +.glyphicon-compressed:before { + content: "\e181"; +} +.glyphicon-earphone:before { + content: "\e182"; +} +.glyphicon-phone-alt:before { + content: "\e183"; +} +.glyphicon-tower:before { + content: "\e184"; +} +.glyphicon-stats:before { + content: "\e185"; +} +.glyphicon-sd-video:before { + content: "\e186"; +} +.glyphicon-hd-video:before { + content: "\e187"; +} +.glyphicon-subtitles:before { + content: "\e188"; +} +.glyphicon-sound-stereo:before { + content: "\e189"; +} +.glyphicon-sound-dolby:before { + content: "\e190"; +} +.glyphicon-sound-5-1:before { + content: "\e191"; +} +.glyphicon-sound-6-1:before { + content: "\e192"; +} +.glyphicon-sound-7-1:before { + content: "\e193"; +} +.glyphicon-copyright-mark:before { + content: "\e194"; +} +.glyphicon-registration-mark:before { + content: "\e195"; +} +.glyphicon-cloud-download:before { + content: "\e197"; +} +.glyphicon-cloud-upload:before { + content: "\e198"; +} +.glyphicon-tree-conifer:before { + content: "\e199"; +} +.glyphicon-tree-deciduous:before { + content: "\e200"; +} +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +*:before, +*:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +html { + font-size: 10px; + + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + line-height: 1.42857143; + color: #333; + background-color: #fff; +} +input, +button, +select, +textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit; +} +a { + color: #337ab7; + text-decoration: none; +} +a:hover, +a:focus { + color: #23527c; + text-decoration: underline; +} +a:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +figure { + margin: 0; +} +img { + vertical-align: middle; +} +.img-responsive, +.thumbnail > img, +.thumbnail a > img, +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + display: block; + max-width: 100%; + height: auto; +} +.img-rounded { + border-radius: 6px; +} +.img-thumbnail { + display: inline-block; + max-width: 100%; + height: auto; + padding: 4px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: all .2s ease-in-out; + -o-transition: all .2s ease-in-out; + transition: all .2s ease-in-out; +} +.img-circle { + border-radius: 50%; +} +hr { + margin-top: 20px; + margin-bottom: 20px; + border: 0; + border-top: 1px solid #eee; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} +.sr-only-focusable:active, +.sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + margin: 0; + overflow: visible; + clip: auto; +} +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + font-family: inherit; + font-weight: 500; + line-height: 1.1; + color: inherit; +} +h1 small, +h2 small, +h3 small, +h4 small, +h5 small, +h6 small, +.h1 small, +.h2 small, +.h3 small, +.h4 small, +.h5 small, +.h6 small, +h1 .small, +h2 .small, +h3 .small, +h4 .small, +h5 .small, +h6 .small, +.h1 .small, +.h2 .small, +.h3 .small, +.h4 .small, +.h5 .small, +.h6 .small { + font-weight: normal; + line-height: 1; + color: #777; +} +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 10px; +} +h1 small, +.h1 small, +h2 small, +.h2 small, +h3 small, +.h3 small, +h1 .small, +.h1 .small, +h2 .small, +.h2 .small, +h3 .small, +.h3 .small { + font-size: 65%; +} +h4, +.h4, +h5, +.h5, +h6, +.h6 { + margin-top: 10px; + margin-bottom: 10px; +} +h4 small, +.h4 small, +h5 small, +.h5 small, +h6 small, +.h6 small, +h4 .small, +.h4 .small, +h5 .small, +.h5 .small, +h6 .small, +.h6 .small { + font-size: 75%; +} +h1, +.h1 { + font-size: 36px; +} +h2, +.h2 { + font-size: 30px; +} +h3, +.h3 { + font-size: 24px; +} +h4, +.h4 { + font-size: 18px; +} +h5, +.h5 { + font-size: 14px; +} +h6, +.h6 { + font-size: 12px; +} +p { + margin: 0 0 10px; +} +.lead { + margin-bottom: 20px; + font-size: 16px; + font-weight: 300; + line-height: 1.4; +} +@media (min-width: 768px) { + .lead { + font-size: 21px; + } +} +small, +.small { + font-size: 85%; +} +mark, +.mark { + padding: .2em; + background-color: #fcf8e3; +} +.text-left { + text-align: left; +} +.text-right { + text-align: right; +} +.text-center { + text-align: center; +} +.text-justify { + text-align: justify; +} +.text-nowrap { + white-space: nowrap; +} +.text-lowercase { + text-transform: lowercase; +} +.text-uppercase { + text-transform: uppercase; +} +.text-capitalize { + text-transform: capitalize; +} +.text-muted { + color: #777; +} +.text-primary { + color: #337ab7; +} +a.text-primary:hover { + color: #286090; +} +.text-success { + color: #3c763d; +} +a.text-success:hover { + color: #2b542c; +} +.text-info { + color: #31708f; +} +a.text-info:hover { + color: #245269; +} +.text-warning { + color: #8a6d3b; +} +a.text-warning:hover { + color: #66512c; +} +.text-danger { + color: #a94442; +} +a.text-danger:hover { + color: #843534; +} +.bg-primary { + color: #fff; + background-color: #337ab7; +} +a.bg-primary:hover { + background-color: #286090; +} +.bg-success { + background-color: #dff0d8; +} +a.bg-success:hover { + background-color: #c1e2b3; +} +.bg-info { + background-color: #d9edf7; +} +a.bg-info:hover { + background-color: #afd9ee; +} +.bg-warning { + background-color: #fcf8e3; +} +a.bg-warning:hover { + background-color: #f7ecb5; +} +.bg-danger { + background-color: #f2dede; +} +a.bg-danger:hover { + background-color: #e4b9b9; +} +.page-header { + padding-bottom: 9px; + margin: 40px 0 20px; + border-bottom: 1px solid #eee; +} +ul, +ol { + margin-top: 0; + margin-bottom: 10px; +} +ul ul, +ol ul, +ul ol, +ol ol { + margin-bottom: 0; +} +.list-unstyled { + padding-left: 0; + list-style: none; +} +.list-inline { + padding-left: 0; + margin-left: -5px; + list-style: none; +} +.list-inline > li { + display: inline-block; + padding-right: 5px; + padding-left: 5px; +} +dl { + margin-top: 0; + margin-bottom: 20px; +} +dt, +dd { + line-height: 1.42857143; +} +dt { + font-weight: bold; +} +dd { + margin-left: 0; +} +@media (min-width: 768px) { + .dl-horizontal dt { + float: left; + width: 160px; + overflow: hidden; + clear: left; + text-align: right; + text-overflow: ellipsis; + white-space: nowrap; + } + .dl-horizontal dd { + margin-left: 180px; + } +} +abbr[title], +abbr[data-original-title] { + cursor: help; + border-bottom: 1px dotted #777; +} +.initialism { + font-size: 90%; + text-transform: uppercase; +} +blockquote { + padding: 10px 20px; + margin: 0 0 20px; + font-size: 17.5px; + border-left: 5px solid #eee; +} +blockquote p:last-child, +blockquote ul:last-child, +blockquote ol:last-child { + margin-bottom: 0; +} +blockquote footer, +blockquote small, +blockquote .small { + display: block; + font-size: 80%; + line-height: 1.42857143; + color: #777; +} +blockquote footer:before, +blockquote small:before, +blockquote .small:before { + content: '\2014 \00A0'; +} +.blockquote-reverse, +blockquote.pull-right { + padding-right: 15px; + padding-left: 0; + text-align: right; + border-right: 5px solid #eee; + border-left: 0; +} +.blockquote-reverse footer:before, +blockquote.pull-right footer:before, +.blockquote-reverse small:before, +blockquote.pull-right small:before, +.blockquote-reverse .small:before, +blockquote.pull-right .small:before { + content: ''; +} +.blockquote-reverse footer:after, +blockquote.pull-right footer:after, +.blockquote-reverse small:after, +blockquote.pull-right small:after, +.blockquote-reverse .small:after, +blockquote.pull-right .small:after { + content: '\00A0 \2014'; +} +address { + margin-bottom: 20px; + font-style: normal; + line-height: 1.42857143; +} +code, +kbd, +pre, +samp { + font-family: Menlo, Monaco, Consolas, "Courier New", monospace; +} +code { + padding: 2px 4px; + font-size: 90%; + color: #c7254e; + background-color: #f9f2f4; + border-radius: 4px; +} +kbd { + padding: 2px 4px; + font-size: 90%; + color: #fff; + background-color: #333; + border-radius: 3px; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); +} +kbd kbd { + padding: 0; + font-size: 100%; + font-weight: bold; + -webkit-box-shadow: none; + box-shadow: none; +} +pre { + display: block; + padding: 9.5px; + margin: 0 0 10px; + font-size: 13px; + line-height: 1.42857143; + color: #333; + word-break: break-all; + word-wrap: break-word; + background-color: #f5f5f5; + border: 1px solid #ccc; + border-radius: 4px; +} +pre code { + padding: 0; + font-size: inherit; + color: inherit; + white-space: pre-wrap; + background-color: transparent; + border-radius: 0; +} +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} +.container { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} +@media (min-width: 768px) { + .container { + width: 750px; + } +} +@media (min-width: 992px) { + .container { + width: 970px; + } +} +@media (min-width: 1200px) { + .container { + width: 1170px; + } +} +.container-fluid { + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} +.row { + margin-right: -15px; + margin-left: -15px; +} +.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { + position: relative; + min-height: 1px; + padding-right: 15px; + padding-left: 15px; +} +.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { + float: left; +} +.col-xs-12 { + width: 100%; +} +.col-xs-11 { + width: 91.66666667%; +} +.col-xs-10 { + width: 83.33333333%; +} +.col-xs-9 { + width: 75%; +} +.col-xs-8 { + width: 66.66666667%; +} +.col-xs-7 { + width: 58.33333333%; +} +.col-xs-6 { + width: 50%; +} +.col-xs-5 { + width: 41.66666667%; +} +.col-xs-4 { + width: 33.33333333%; +} +.col-xs-3 { + width: 25%; +} +.col-xs-2 { + width: 16.66666667%; +} +.col-xs-1 { + width: 8.33333333%; +} +.col-xs-pull-12 { + right: 100%; +} +.col-xs-pull-11 { + right: 91.66666667%; +} +.col-xs-pull-10 { + right: 83.33333333%; +} +.col-xs-pull-9 { + right: 75%; +} +.col-xs-pull-8 { + right: 66.66666667%; +} +.col-xs-pull-7 { + right: 58.33333333%; +} +.col-xs-pull-6 { + right: 50%; +} +.col-xs-pull-5 { + right: 41.66666667%; +} +.col-xs-pull-4 { + right: 33.33333333%; +} +.col-xs-pull-3 { + right: 25%; +} +.col-xs-pull-2 { + right: 16.66666667%; +} +.col-xs-pull-1 { + right: 8.33333333%; +} +.col-xs-pull-0 { + right: auto; +} +.col-xs-push-12 { + left: 100%; +} +.col-xs-push-11 { + left: 91.66666667%; +} +.col-xs-push-10 { + left: 83.33333333%; +} +.col-xs-push-9 { + left: 75%; +} +.col-xs-push-8 { + left: 66.66666667%; +} +.col-xs-push-7 { + left: 58.33333333%; +} +.col-xs-push-6 { + left: 50%; +} +.col-xs-push-5 { + left: 41.66666667%; +} +.col-xs-push-4 { + left: 33.33333333%; +} +.col-xs-push-3 { + left: 25%; +} +.col-xs-push-2 { + left: 16.66666667%; +} +.col-xs-push-1 { + left: 8.33333333%; +} +.col-xs-push-0 { + left: auto; +} +.col-xs-offset-12 { + margin-left: 100%; +} +.col-xs-offset-11 { + margin-left: 91.66666667%; +} +.col-xs-offset-10 { + margin-left: 83.33333333%; +} +.col-xs-offset-9 { + margin-left: 75%; +} +.col-xs-offset-8 { + margin-left: 66.66666667%; +} +.col-xs-offset-7 { + margin-left: 58.33333333%; +} +.col-xs-offset-6 { + margin-left: 50%; +} +.col-xs-offset-5 { + margin-left: 41.66666667%; +} +.col-xs-offset-4 { + margin-left: 33.33333333%; +} +.col-xs-offset-3 { + margin-left: 25%; +} +.col-xs-offset-2 { + margin-left: 16.66666667%; +} +.col-xs-offset-1 { + margin-left: 8.33333333%; +} +.col-xs-offset-0 { + margin-left: 0; +} +@media (min-width: 768px) { + .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { + float: left; + } + .col-sm-12 { + width: 100%; + } + .col-sm-11 { + width: 91.66666667%; + } + .col-sm-10 { + width: 83.33333333%; + } + .col-sm-9 { + width: 75%; + } + .col-sm-8 { + width: 66.66666667%; + } + .col-sm-7 { + width: 58.33333333%; + } + .col-sm-6 { + width: 50%; + } + .col-sm-5 { + width: 41.66666667%; + } + .col-sm-4 { + width: 33.33333333%; + } + .col-sm-3 { + width: 25%; + } + .col-sm-2 { + width: 16.66666667%; + } + .col-sm-1 { + width: 8.33333333%; + } + .col-sm-pull-12 { + right: 100%; + } + .col-sm-pull-11 { + right: 91.66666667%; + } + .col-sm-pull-10 { + right: 83.33333333%; + } + .col-sm-pull-9 { + right: 75%; + } + .col-sm-pull-8 { + right: 66.66666667%; + } + .col-sm-pull-7 { + right: 58.33333333%; + } + .col-sm-pull-6 { + right: 50%; + } + .col-sm-pull-5 { + right: 41.66666667%; + } + .col-sm-pull-4 { + right: 33.33333333%; + } + .col-sm-pull-3 { + right: 25%; + } + .col-sm-pull-2 { + right: 16.66666667%; + } + .col-sm-pull-1 { + right: 8.33333333%; + } + .col-sm-pull-0 { + right: auto; + } + .col-sm-push-12 { + left: 100%; + } + .col-sm-push-11 { + left: 91.66666667%; + } + .col-sm-push-10 { + left: 83.33333333%; + } + .col-sm-push-9 { + left: 75%; + } + .col-sm-push-8 { + left: 66.66666667%; + } + .col-sm-push-7 { + left: 58.33333333%; + } + .col-sm-push-6 { + left: 50%; + } + .col-sm-push-5 { + left: 41.66666667%; + } + .col-sm-push-4 { + left: 33.33333333%; + } + .col-sm-push-3 { + left: 25%; + } + .col-sm-push-2 { + left: 16.66666667%; + } + .col-sm-push-1 { + left: 8.33333333%; + } + .col-sm-push-0 { + left: auto; + } + .col-sm-offset-12 { + margin-left: 100%; + } + .col-sm-offset-11 { + margin-left: 91.66666667%; + } + .col-sm-offset-10 { + margin-left: 83.33333333%; + } + .col-sm-offset-9 { + margin-left: 75%; + } + .col-sm-offset-8 { + margin-left: 66.66666667%; + } + .col-sm-offset-7 { + margin-left: 58.33333333%; + } + .col-sm-offset-6 { + margin-left: 50%; + } + .col-sm-offset-5 { + margin-left: 41.66666667%; + } + .col-sm-offset-4 { + margin-left: 33.33333333%; + } + .col-sm-offset-3 { + margin-left: 25%; + } + .col-sm-offset-2 { + margin-left: 16.66666667%; + } + .col-sm-offset-1 { + margin-left: 8.33333333%; + } + .col-sm-offset-0 { + margin-left: 0; + } +} +@media (min-width: 992px) { + .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { + float: left; + } + .col-md-12 { + width: 100%; + } + .col-md-11 { + width: 91.66666667%; + } + .col-md-10 { + width: 83.33333333%; + } + .col-md-9 { + width: 75%; + } + .col-md-8 { + width: 66.66666667%; + } + .col-md-7 { + width: 58.33333333%; + } + .col-md-6 { + width: 50%; + } + .col-md-5 { + width: 41.66666667%; + } + .col-md-4 { + width: 33.33333333%; + } + .col-md-3 { + width: 25%; + } + .col-md-2 { + width: 16.66666667%; + } + .col-md-1 { + width: 8.33333333%; + } + .col-md-pull-12 { + right: 100%; + } + .col-md-pull-11 { + right: 91.66666667%; + } + .col-md-pull-10 { + right: 83.33333333%; + } + .col-md-pull-9 { + right: 75%; + } + .col-md-pull-8 { + right: 66.66666667%; + } + .col-md-pull-7 { + right: 58.33333333%; + } + .col-md-pull-6 { + right: 50%; + } + .col-md-pull-5 { + right: 41.66666667%; + } + .col-md-pull-4 { + right: 33.33333333%; + } + .col-md-pull-3 { + right: 25%; + } + .col-md-pull-2 { + right: 16.66666667%; + } + .col-md-pull-1 { + right: 8.33333333%; + } + .col-md-pull-0 { + right: auto; + } + .col-md-push-12 { + left: 100%; + } + .col-md-push-11 { + left: 91.66666667%; + } + .col-md-push-10 { + left: 83.33333333%; + } + .col-md-push-9 { + left: 75%; + } + .col-md-push-8 { + left: 66.66666667%; + } + .col-md-push-7 { + left: 58.33333333%; + } + .col-md-push-6 { + left: 50%; + } + .col-md-push-5 { + left: 41.66666667%; + } + .col-md-push-4 { + left: 33.33333333%; + } + .col-md-push-3 { + left: 25%; + } + .col-md-push-2 { + left: 16.66666667%; + } + .col-md-push-1 { + left: 8.33333333%; + } + .col-md-push-0 { + left: auto; + } + .col-md-offset-12 { + margin-left: 100%; + } + .col-md-offset-11 { + margin-left: 91.66666667%; + } + .col-md-offset-10 { + margin-left: 83.33333333%; + } + .col-md-offset-9 { + margin-left: 75%; + } + .col-md-offset-8 { + margin-left: 66.66666667%; + } + .col-md-offset-7 { + margin-left: 58.33333333%; + } + .col-md-offset-6 { + margin-left: 50%; + } + .col-md-offset-5 { + margin-left: 41.66666667%; + } + .col-md-offset-4 { + margin-left: 33.33333333%; + } + .col-md-offset-3 { + margin-left: 25%; + } + .col-md-offset-2 { + margin-left: 16.66666667%; + } + .col-md-offset-1 { + margin-left: 8.33333333%; + } + .col-md-offset-0 { + margin-left: 0; + } +} +@media (min-width: 1200px) { + .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { + float: left; + } + .col-lg-12 { + width: 100%; + } + .col-lg-11 { + width: 91.66666667%; + } + .col-lg-10 { + width: 83.33333333%; + } + .col-lg-9 { + width: 75%; + } + .col-lg-8 { + width: 66.66666667%; + } + .col-lg-7 { + width: 58.33333333%; + } + .col-lg-6 { + width: 50%; + } + .col-lg-5 { + width: 41.66666667%; + } + .col-lg-4 { + width: 33.33333333%; + } + .col-lg-3 { + width: 25%; + } + .col-lg-2 { + width: 16.66666667%; + } + .col-lg-1 { + width: 8.33333333%; + } + .col-lg-pull-12 { + right: 100%; + } + .col-lg-pull-11 { + right: 91.66666667%; + } + .col-lg-pull-10 { + right: 83.33333333%; + } + .col-lg-pull-9 { + right: 75%; + } + .col-lg-pull-8 { + right: 66.66666667%; + } + .col-lg-pull-7 { + right: 58.33333333%; + } + .col-lg-pull-6 { + right: 50%; + } + .col-lg-pull-5 { + right: 41.66666667%; + } + .col-lg-pull-4 { + right: 33.33333333%; + } + .col-lg-pull-3 { + right: 25%; + } + .col-lg-pull-2 { + right: 16.66666667%; + } + .col-lg-pull-1 { + right: 8.33333333%; + } + .col-lg-pull-0 { + right: auto; + } + .col-lg-push-12 { + left: 100%; + } + .col-lg-push-11 { + left: 91.66666667%; + } + .col-lg-push-10 { + left: 83.33333333%; + } + .col-lg-push-9 { + left: 75%; + } + .col-lg-push-8 { + left: 66.66666667%; + } + .col-lg-push-7 { + left: 58.33333333%; + } + .col-lg-push-6 { + left: 50%; + } + .col-lg-push-5 { + left: 41.66666667%; + } + .col-lg-push-4 { + left: 33.33333333%; + } + .col-lg-push-3 { + left: 25%; + } + .col-lg-push-2 { + left: 16.66666667%; + } + .col-lg-push-1 { + left: 8.33333333%; + } + .col-lg-push-0 { + left: auto; + } + .col-lg-offset-12 { + margin-left: 100%; + } + .col-lg-offset-11 { + margin-left: 91.66666667%; + } + .col-lg-offset-10 { + margin-left: 83.33333333%; + } + .col-lg-offset-9 { + margin-left: 75%; + } + .col-lg-offset-8 { + margin-left: 66.66666667%; + } + .col-lg-offset-7 { + margin-left: 58.33333333%; + } + .col-lg-offset-6 { + margin-left: 50%; + } + .col-lg-offset-5 { + margin-left: 41.66666667%; + } + .col-lg-offset-4 { + margin-left: 33.33333333%; + } + .col-lg-offset-3 { + margin-left: 25%; + } + .col-lg-offset-2 { + margin-left: 16.66666667%; + } + .col-lg-offset-1 { + margin-left: 8.33333333%; + } + .col-lg-offset-0 { + margin-left: 0; + } +} +table { + background-color: transparent; +} +caption { + padding-top: 8px; + padding-bottom: 8px; + color: #777; + text-align: left; +} +th { + text-align: left; +} +.table { + width: 100%; + max-width: 100%; + margin-bottom: 20px; +} +.table > thead > tr > th, +.table > tbody > tr > th, +.table > tfoot > tr > th, +.table > thead > tr > td, +.table > tbody > tr > td, +.table > tfoot > tr > td { + padding: 8px; + line-height: 1.42857143; + vertical-align: top; + border-top: 1px solid #ddd; +} +.table > thead > tr > th { + vertical-align: bottom; + border-bottom: 2px solid #ddd; +} +.table > caption + thead > tr:first-child > th, +.table > colgroup + thead > tr:first-child > th, +.table > thead:first-child > tr:first-child > th, +.table > caption + thead > tr:first-child > td, +.table > colgroup + thead > tr:first-child > td, +.table > thead:first-child > tr:first-child > td { + border-top: 0; +} +.table > tbody + tbody { + border-top: 2px solid #ddd; +} +.table .table { + background-color: #fff; +} +.table-condensed > thead > tr > th, +.table-condensed > tbody > tr > th, +.table-condensed > tfoot > tr > th, +.table-condensed > thead > tr > td, +.table-condensed > tbody > tr > td, +.table-condensed > tfoot > tr > td { + padding: 5px; +} +.table-bordered { + border: 1px solid #ddd; +} +.table-bordered > thead > tr > th, +.table-bordered > tbody > tr > th, +.table-bordered > tfoot > tr > th, +.table-bordered > thead > tr > td, +.table-bordered > tbody > tr > td, +.table-bordered > tfoot > tr > td { + border: 1px solid #ddd; +} +.table-bordered > thead > tr > th, +.table-bordered > thead > tr > td { + border-bottom-width: 2px; +} +.table-striped > tbody > tr:nth-child(odd) { + background-color: #f9f9f9; +} +.table-hover > tbody > tr:hover { + background-color: #f5f5f5; +} +table col[class*="col-"] { + position: static; + display: table-column; + float: none; +} +table td[class*="col-"], +table th[class*="col-"] { + position: static; + display: table-cell; + float: none; +} +.table > thead > tr > td.active, +.table > tbody > tr > td.active, +.table > tfoot > tr > td.active, +.table > thead > tr > th.active, +.table > tbody > tr > th.active, +.table > tfoot > tr > th.active, +.table > thead > tr.active > td, +.table > tbody > tr.active > td, +.table > tfoot > tr.active > td, +.table > thead > tr.active > th, +.table > tbody > tr.active > th, +.table > tfoot > tr.active > th { + background-color: #f5f5f5; +} +.table-hover > tbody > tr > td.active:hover, +.table-hover > tbody > tr > th.active:hover, +.table-hover > tbody > tr.active:hover > td, +.table-hover > tbody > tr:hover > .active, +.table-hover > tbody > tr.active:hover > th { + background-color: #e8e8e8; +} +.table > thead > tr > td.success, +.table > tbody > tr > td.success, +.table > tfoot > tr > td.success, +.table > thead > tr > th.success, +.table > tbody > tr > th.success, +.table > tfoot > tr > th.success, +.table > thead > tr.success > td, +.table > tbody > tr.success > td, +.table > tfoot > tr.success > td, +.table > thead > tr.success > th, +.table > tbody > tr.success > th, +.table > tfoot > tr.success > th { + background-color: #dff0d8; +} +.table-hover > tbody > tr > td.success:hover, +.table-hover > tbody > tr > th.success:hover, +.table-hover > tbody > tr.success:hover > td, +.table-hover > tbody > tr:hover > .success, +.table-hover > tbody > tr.success:hover > th { + background-color: #d0e9c6; +} +.table > thead > tr > td.info, +.table > tbody > tr > td.info, +.table > tfoot > tr > td.info, +.table > thead > tr > th.info, +.table > tbody > tr > th.info, +.table > tfoot > tr > th.info, +.table > thead > tr.info > td, +.table > tbody > tr.info > td, +.table > tfoot > tr.info > td, +.table > thead > tr.info > th, +.table > tbody > tr.info > th, +.table > tfoot > tr.info > th { + background-color: #d9edf7; +} +.table-hover > tbody > tr > td.info:hover, +.table-hover > tbody > tr > th.info:hover, +.table-hover > tbody > tr.info:hover > td, +.table-hover > tbody > tr:hover > .info, +.table-hover > tbody > tr.info:hover > th { + background-color: #c4e3f3; +} +.table > thead > tr > td.warning, +.table > tbody > tr > td.warning, +.table > tfoot > tr > td.warning, +.table > thead > tr > th.warning, +.table > tbody > tr > th.warning, +.table > tfoot > tr > th.warning, +.table > thead > tr.warning > td, +.table > tbody > tr.warning > td, +.table > tfoot > tr.warning > td, +.table > thead > tr.warning > th, +.table > tbody > tr.warning > th, +.table > tfoot > tr.warning > th { + background-color: #fcf8e3; +} +.table-hover > tbody > tr > td.warning:hover, +.table-hover > tbody > tr > th.warning:hover, +.table-hover > tbody > tr.warning:hover > td, +.table-hover > tbody > tr:hover > .warning, +.table-hover > tbody > tr.warning:hover > th { + background-color: #faf2cc; +} +.table > thead > tr > td.danger, +.table > tbody > tr > td.danger, +.table > tfoot > tr > td.danger, +.table > thead > tr > th.danger, +.table > tbody > tr > th.danger, +.table > tfoot > tr > th.danger, +.table > thead > tr.danger > td, +.table > tbody > tr.danger > td, +.table > tfoot > tr.danger > td, +.table > thead > tr.danger > th, +.table > tbody > tr.danger > th, +.table > tfoot > tr.danger > th { + background-color: #f2dede; +} +.table-hover > tbody > tr > td.danger:hover, +.table-hover > tbody > tr > th.danger:hover, +.table-hover > tbody > tr.danger:hover > td, +.table-hover > tbody > tr:hover > .danger, +.table-hover > tbody > tr.danger:hover > th { + background-color: #ebcccc; +} +.table-responsive { + min-height: .01%; + overflow-x: auto; +} +@media screen and (max-width: 767px) { + .table-responsive { + width: 100%; + margin-bottom: 15px; + overflow-y: hidden; + -ms-overflow-style: -ms-autohiding-scrollbar; + border: 1px solid #ddd; + } + .table-responsive > .table { + margin-bottom: 0; + } + .table-responsive > .table > thead > tr > th, + .table-responsive > .table > tbody > tr > th, + .table-responsive > .table > tfoot > tr > th, + .table-responsive > .table > thead > tr > td, + .table-responsive > .table > tbody > tr > td, + .table-responsive > .table > tfoot > tr > td { + white-space: nowrap; + } + .table-responsive > .table-bordered { + border: 0; + } + .table-responsive > .table-bordered > thead > tr > th:first-child, + .table-responsive > .table-bordered > tbody > tr > th:first-child, + .table-responsive > .table-bordered > tfoot > tr > th:first-child, + .table-responsive > .table-bordered > thead > tr > td:first-child, + .table-responsive > .table-bordered > tbody > tr > td:first-child, + .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; + } + .table-responsive > .table-bordered > thead > tr > th:last-child, + .table-responsive > .table-bordered > tbody > tr > th:last-child, + .table-responsive > .table-bordered > tfoot > tr > th:last-child, + .table-responsive > .table-bordered > thead > tr > td:last-child, + .table-responsive > .table-bordered > tbody > tr > td:last-child, + .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; + } + .table-responsive > .table-bordered > tbody > tr:last-child > th, + .table-responsive > .table-bordered > tfoot > tr:last-child > th, + .table-responsive > .table-bordered > tbody > tr:last-child > td, + .table-responsive > .table-bordered > tfoot > tr:last-child > td { + border-bottom: 0; + } +} +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0; +} +legend { + display: block; + width: 100%; + padding: 0; + margin-bottom: 20px; + font-size: 21px; + line-height: inherit; + color: #333; + border: 0; + border-bottom: 1px solid #e5e5e5; +} +label { + display: inline-block; + max-width: 100%; + margin-bottom: 5px; + font-weight: bold; +} +input[type="search"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +input[type="radio"], +input[type="checkbox"] { + margin: 4px 0 0; + margin-top: 1px \9; + line-height: normal; +} +input[type="file"] { + display: block; +} +input[type="range"] { + display: block; + width: 100%; +} +select[multiple], +select[size] { + height: auto; +} +input[type="file"]:focus, +input[type="radio"]:focus, +input[type="checkbox"]:focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +output { + display: block; + padding-top: 7px; + font-size: 14px; + line-height: 1.42857143; + color: #555; +} +.form-control { + display: block; + width: 100%; + height: 34px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.42857143; + color: #555; + background-color: #fff; + background-image: none; + border: 1px solid #ccc; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; + -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; +} +.form-control:focus { + border-color: #66afe9; + outline: 0; + -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); + box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); +} +.form-control::-moz-placeholder { + color: #999; + opacity: 1; +} +.form-control:-ms-input-placeholder { + color: #999; +} +.form-control::-webkit-input-placeholder { + color: #999; +} +.form-control[disabled], +.form-control[readonly], +fieldset[disabled] .form-control { + cursor: not-allowed; + background-color: #eee; + opacity: 1; +} +textarea.form-control { + height: auto; +} +input[type="search"] { + -webkit-appearance: none; +} +@media screen and (-webkit-min-device-pixel-ratio: 0) { + input[type="date"], + input[type="time"], + input[type="datetime-local"], + input[type="month"] { + line-height: 34px; + } + input[type="date"].input-sm, + input[type="time"].input-sm, + input[type="datetime-local"].input-sm, + input[type="month"].input-sm { + line-height: 30px; + } + input[type="date"].input-lg, + input[type="time"].input-lg, + input[type="datetime-local"].input-lg, + input[type="month"].input-lg { + line-height: 46px; + } +} +.form-group { + margin-bottom: 15px; +} +.radio, +.checkbox { + position: relative; + display: block; + margin-top: 10px; + margin-bottom: 10px; +} +.radio label, +.checkbox label { + min-height: 20px; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + cursor: pointer; +} +.radio input[type="radio"], +.radio-inline input[type="radio"], +.checkbox input[type="checkbox"], +.checkbox-inline input[type="checkbox"] { + position: absolute; + margin-top: 4px \9; + margin-left: -20px; +} +.radio + .radio, +.checkbox + .checkbox { + margin-top: -5px; +} +.radio-inline, +.checkbox-inline { + display: inline-block; + padding-left: 20px; + margin-bottom: 0; + font-weight: normal; + vertical-align: middle; + cursor: pointer; +} +.radio-inline + .radio-inline, +.checkbox-inline + .checkbox-inline { + margin-top: 0; + margin-left: 10px; +} +input[type="radio"][disabled], +input[type="checkbox"][disabled], +input[type="radio"].disabled, +input[type="checkbox"].disabled, +fieldset[disabled] input[type="radio"], +fieldset[disabled] input[type="checkbox"] { + cursor: not-allowed; +} +.radio-inline.disabled, +.checkbox-inline.disabled, +fieldset[disabled] .radio-inline, +fieldset[disabled] .checkbox-inline { + cursor: not-allowed; +} +.radio.disabled label, +.checkbox.disabled label, +fieldset[disabled] .radio label, +fieldset[disabled] .checkbox label { + cursor: not-allowed; +} +.form-control-static { + padding-top: 7px; + padding-bottom: 7px; + margin-bottom: 0; +} +.form-control-static.input-lg, +.form-control-static.input-sm { + padding-right: 0; + padding-left: 0; +} +.input-sm, +.form-group-sm .form-control { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-sm, +select.form-group-sm .form-control { + height: 30px; + line-height: 30px; +} +textarea.input-sm, +textarea.form-group-sm .form-control, +select[multiple].input-sm, +select[multiple].form-group-sm .form-control { + height: auto; +} +.input-lg, +.form-group-lg .form-control { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} +select.input-lg, +select.form-group-lg .form-control { + height: 46px; + line-height: 46px; +} +textarea.input-lg, +textarea.form-group-lg .form-control, +select[multiple].input-lg, +select[multiple].form-group-lg .form-control { + height: auto; +} +.has-feedback { + position: relative; +} +.has-feedback .form-control { + padding-right: 42.5px; +} +.form-control-feedback { + position: absolute; + top: 0; + right: 0; + z-index: 2; + display: block; + width: 34px; + height: 34px; + line-height: 34px; + text-align: center; + pointer-events: none; +} +.input-lg + .form-control-feedback { + width: 46px; + height: 46px; + line-height: 46px; +} +.input-sm + .form-control-feedback { + width: 30px; + height: 30px; + line-height: 30px; +} +.has-success .help-block, +.has-success .control-label, +.has-success .radio, +.has-success .checkbox, +.has-success .radio-inline, +.has-success .checkbox-inline, +.has-success.radio label, +.has-success.checkbox label, +.has-success.radio-inline label, +.has-success.checkbox-inline label { + color: #3c763d; +} +.has-success .form-control { + border-color: #3c763d; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-success .form-control:focus { + border-color: #2b542c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; +} +.has-success .input-group-addon { + color: #3c763d; + background-color: #dff0d8; + border-color: #3c763d; +} +.has-success .form-control-feedback { + color: #3c763d; +} +.has-warning .help-block, +.has-warning .control-label, +.has-warning .radio, +.has-warning .checkbox, +.has-warning .radio-inline, +.has-warning .checkbox-inline, +.has-warning.radio label, +.has-warning.checkbox label, +.has-warning.radio-inline label, +.has-warning.checkbox-inline label { + color: #8a6d3b; +} +.has-warning .form-control { + border-color: #8a6d3b; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-warning .form-control:focus { + border-color: #66512c; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; +} +.has-warning .input-group-addon { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #8a6d3b; +} +.has-warning .form-control-feedback { + color: #8a6d3b; +} +.has-error .help-block, +.has-error .control-label, +.has-error .radio, +.has-error .checkbox, +.has-error .radio-inline, +.has-error .checkbox-inline, +.has-error.radio label, +.has-error.checkbox label, +.has-error.radio-inline label, +.has-error.checkbox-inline label { + color: #a94442; +} +.has-error .form-control { + border-color: #a94442; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); +} +.has-error .form-control:focus { + border-color: #843534; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; +} +.has-error .input-group-addon { + color: #a94442; + background-color: #f2dede; + border-color: #a94442; +} +.has-error .form-control-feedback { + color: #a94442; +} +.has-feedback label ~ .form-control-feedback { + top: 25px; +} +.has-feedback label.sr-only ~ .form-control-feedback { + top: 0; +} +.help-block { + display: block; + margin-top: 5px; + margin-bottom: 10px; + color: #737373; +} +@media (min-width: 768px) { + .form-inline .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .form-inline .form-control-static { + display: inline-block; + } + .form-inline .input-group { + display: inline-table; + vertical-align: middle; + } + .form-inline .input-group .input-group-addon, + .form-inline .input-group .input-group-btn, + .form-inline .input-group .form-control { + width: auto; + } + .form-inline .input-group > .form-control { + width: 100%; + } + .form-inline .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio, + .form-inline .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .form-inline .radio label, + .form-inline .checkbox label { + padding-left: 0; + } + .form-inline .radio input[type="radio"], + .form-inline .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .form-inline .has-feedback .form-control-feedback { + top: 0; + } +} +.form-horizontal .radio, +.form-horizontal .checkbox, +.form-horizontal .radio-inline, +.form-horizontal .checkbox-inline { + padding-top: 7px; + margin-top: 0; + margin-bottom: 0; +} +.form-horizontal .radio, +.form-horizontal .checkbox { + min-height: 27px; +} +.form-horizontal .form-group { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .form-horizontal .control-label { + padding-top: 7px; + margin-bottom: 0; + text-align: right; + } +} +.form-horizontal .has-feedback .form-control-feedback { + right: 15px; +} +@media (min-width: 768px) { + .form-horizontal .form-group-lg .control-label { + padding-top: 14.3px; + } +} +@media (min-width: 768px) { + .form-horizontal .form-group-sm .control-label { + padding-top: 6px; + } +} +.btn { + display: inline-block; + padding: 6px 12px; + margin-bottom: 0; + font-size: 14px; + font-weight: normal; + line-height: 1.42857143; + text-align: center; + white-space: nowrap; + vertical-align: middle; + -ms-touch-action: manipulation; + touch-action: manipulation; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.btn:focus, +.btn:active:focus, +.btn.active:focus, +.btn.focus, +.btn:active.focus, +.btn.active.focus { + outline: thin dotted; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} +.btn:hover, +.btn:focus, +.btn.focus { + color: #333; + text-decoration: none; +} +.btn:active, +.btn.active { + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn.disabled, +.btn[disabled], +fieldset[disabled] .btn { + pointer-events: none; + cursor: not-allowed; + filter: alpha(opacity=65); + -webkit-box-shadow: none; + box-shadow: none; + opacity: .65; +} +.btn-default { + color: #333; + background-color: #fff; + border-color: #ccc; +} +.btn-default:hover, +.btn-default:focus, +.btn-default.focus, +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + color: #333; + background-color: #e6e6e6; + border-color: #adadad; +} +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + background-image: none; +} +.btn-default.disabled, +.btn-default[disabled], +fieldset[disabled] .btn-default, +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled.focus, +.btn-default[disabled].focus, +fieldset[disabled] .btn-default.focus, +.btn-default.disabled:active, +.btn-default[disabled]:active, +fieldset[disabled] .btn-default:active, +.btn-default.disabled.active, +.btn-default[disabled].active, +fieldset[disabled] .btn-default.active { + background-color: #fff; + border-color: #ccc; +} +.btn-default .badge { + color: #fff; + background-color: #333; +} +.btn-primary { + color: #fff; + background-color: #337ab7; + border-color: #2e6da4; +} +.btn-primary:hover, +.btn-primary:focus, +.btn-primary.focus, +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + color: #fff; + background-color: #286090; + border-color: #204d74; +} +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + background-image: none; +} +.btn-primary.disabled, +.btn-primary[disabled], +fieldset[disabled] .btn-primary, +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled.focus, +.btn-primary[disabled].focus, +fieldset[disabled] .btn-primary.focus, +.btn-primary.disabled:active, +.btn-primary[disabled]:active, +fieldset[disabled] .btn-primary:active, +.btn-primary.disabled.active, +.btn-primary[disabled].active, +fieldset[disabled] .btn-primary.active { + background-color: #337ab7; + border-color: #2e6da4; +} +.btn-primary .badge { + color: #337ab7; + background-color: #fff; +} +.btn-success { + color: #fff; + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success:hover, +.btn-success:focus, +.btn-success.focus, +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + color: #fff; + background-color: #449d44; + border-color: #398439; +} +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + background-image: none; +} +.btn-success.disabled, +.btn-success[disabled], +fieldset[disabled] .btn-success, +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled.focus, +.btn-success[disabled].focus, +fieldset[disabled] .btn-success.focus, +.btn-success.disabled:active, +.btn-success[disabled]:active, +fieldset[disabled] .btn-success:active, +.btn-success.disabled.active, +.btn-success[disabled].active, +fieldset[disabled] .btn-success.active { + background-color: #5cb85c; + border-color: #4cae4c; +} +.btn-success .badge { + color: #5cb85c; + background-color: #fff; +} +.btn-info { + color: #fff; + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info:hover, +.btn-info:focus, +.btn-info.focus, +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + color: #fff; + background-color: #31b0d5; + border-color: #269abc; +} +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + background-image: none; +} +.btn-info.disabled, +.btn-info[disabled], +fieldset[disabled] .btn-info, +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled.focus, +.btn-info[disabled].focus, +fieldset[disabled] .btn-info.focus, +.btn-info.disabled:active, +.btn-info[disabled]:active, +fieldset[disabled] .btn-info:active, +.btn-info.disabled.active, +.btn-info[disabled].active, +fieldset[disabled] .btn-info.active { + background-color: #5bc0de; + border-color: #46b8da; +} +.btn-info .badge { + color: #5bc0de; + background-color: #fff; +} +.btn-warning { + color: #fff; + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning:hover, +.btn-warning:focus, +.btn-warning.focus, +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + color: #fff; + background-color: #ec971f; + border-color: #d58512; +} +.btn-warning:active, +.btn-warning.active, +.open > .dropdown-toggle.btn-warning { + background-image: none; +} +.btn-warning.disabled, +.btn-warning[disabled], +fieldset[disabled] .btn-warning, +.btn-warning.disabled:hover, +.btn-warning[disabled]:hover, +fieldset[disabled] .btn-warning:hover, +.btn-warning.disabled:focus, +.btn-warning[disabled]:focus, +fieldset[disabled] .btn-warning:focus, +.btn-warning.disabled.focus, +.btn-warning[disabled].focus, +fieldset[disabled] .btn-warning.focus, +.btn-warning.disabled:active, +.btn-warning[disabled]:active, +fieldset[disabled] .btn-warning:active, +.btn-warning.disabled.active, +.btn-warning[disabled].active, +fieldset[disabled] .btn-warning.active { + background-color: #f0ad4e; + border-color: #eea236; +} +.btn-warning .badge { + color: #f0ad4e; + background-color: #fff; +} +.btn-danger { + color: #fff; + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger:hover, +.btn-danger:focus, +.btn-danger.focus, +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + color: #fff; + background-color: #c9302c; + border-color: #ac2925; +} +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + background-image: none; +} +.btn-danger.disabled, +.btn-danger[disabled], +fieldset[disabled] .btn-danger, +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled.focus, +.btn-danger[disabled].focus, +fieldset[disabled] .btn-danger.focus, +.btn-danger.disabled:active, +.btn-danger[disabled]:active, +fieldset[disabled] .btn-danger:active, +.btn-danger.disabled.active, +.btn-danger[disabled].active, +fieldset[disabled] .btn-danger.active { + background-color: #d9534f; + border-color: #d43f3a; +} +.btn-danger .badge { + color: #d9534f; + background-color: #fff; +} +.btn-link { + font-weight: normal; + color: #337ab7; + border-radius: 0; +} +.btn-link, +.btn-link:active, +.btn-link.active, +.btn-link[disabled], +fieldset[disabled] .btn-link { + background-color: transparent; + -webkit-box-shadow: none; + box-shadow: none; +} +.btn-link, +.btn-link:hover, +.btn-link:focus, +.btn-link:active { + border-color: transparent; +} +.btn-link:hover, +.btn-link:focus { + color: #23527c; + text-decoration: underline; + background-color: transparent; +} +.btn-link[disabled]:hover, +fieldset[disabled] .btn-link:hover, +.btn-link[disabled]:focus, +fieldset[disabled] .btn-link:focus { + color: #777; + text-decoration: none; +} +.btn-lg, +.btn-group-lg > .btn { + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} +.btn-sm, +.btn-group-sm > .btn { + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-xs, +.btn-group-xs > .btn { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +.btn-block { + display: block; + width: 100%; +} +.btn-block + .btn-block { + margin-top: 5px; +} +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} +.fade { + opacity: 0; + -webkit-transition: opacity .15s linear; + -o-transition: opacity .15s linear; + transition: opacity .15s linear; +} +.fade.in { + opacity: 1; +} +.collapse { + display: none; + visibility: hidden; +} +.collapse.in { + display: block; + visibility: visible; +} +tr.collapse.in { + display: table-row; +} +tbody.collapse.in { + display: table-row-group; +} +.collapsing { + position: relative; + height: 0; + overflow: hidden; + -webkit-transition-timing-function: ease; + -o-transition-timing-function: ease; + transition-timing-function: ease; + -webkit-transition-duration: .35s; + -o-transition-duration: .35s; + transition-duration: .35s; + -webkit-transition-property: height, visibility; + -o-transition-property: height, visibility; + transition-property: height, visibility; +} +.caret { + display: inline-block; + width: 0; + height: 0; + margin-left: 2px; + vertical-align: middle; + border-top: 4px solid; + border-right: 4px solid transparent; + border-left: 4px solid transparent; +} +.dropdown { + position: relative; +} +.dropdown-toggle:focus { + outline: 0; +} +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 160px; + padding: 5px 0; + margin: 2px 0 0; + font-size: 14px; + text-align: left; + list-style: none; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .15); + border-radius: 4px; + -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); + box-shadow: 0 6px 12px rgba(0, 0, 0, .175); +} +.dropdown-menu.pull-right { + right: 0; + left: auto; +} +.dropdown-menu .divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.dropdown-menu > li > a { + display: block; + padding: 3px 20px; + clear: both; + font-weight: normal; + line-height: 1.42857143; + color: #333; + white-space: nowrap; +} +.dropdown-menu > li > a:hover, +.dropdown-menu > li > a:focus { + color: #262626; + text-decoration: none; + background-color: #f5f5f5; +} +.dropdown-menu > .active > a, +.dropdown-menu > .active > a:hover, +.dropdown-menu > .active > a:focus { + color: #fff; + text-decoration: none; + background-color: #337ab7; + outline: 0; +} +.dropdown-menu > .disabled > a, +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + color: #777; +} +.dropdown-menu > .disabled > a:hover, +.dropdown-menu > .disabled > a:focus { + text-decoration: none; + cursor: not-allowed; + background-color: transparent; + background-image: none; + filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); +} +.open > .dropdown-menu { + display: block; +} +.open > a { + outline: 0; +} +.dropdown-menu-right { + right: 0; + left: auto; +} +.dropdown-menu-left { + right: auto; + left: 0; +} +.dropdown-header { + display: block; + padding: 3px 20px; + font-size: 12px; + line-height: 1.42857143; + color: #777; + white-space: nowrap; +} +.dropdown-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 990; +} +.pull-right > .dropdown-menu { + right: 0; + left: auto; +} +.dropup .caret, +.navbar-fixed-bottom .dropdown .caret { + content: ""; + border-top: 0; + border-bottom: 4px solid; +} +.dropup .dropdown-menu, +.navbar-fixed-bottom .dropdown .dropdown-menu { + top: auto; + bottom: 100%; + margin-bottom: 1px; +} +@media (min-width: 768px) { + .navbar-right .dropdown-menu { + right: 0; + left: auto; + } + .navbar-right .dropdown-menu-left { + right: auto; + left: 0; + } +} +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-block; + vertical-align: middle; +} +.btn-group > .btn, +.btn-group-vertical > .btn { + position: relative; + float: left; +} +.btn-group > .btn:hover, +.btn-group-vertical > .btn:hover, +.btn-group > .btn:focus, +.btn-group-vertical > .btn:focus, +.btn-group > .btn:active, +.btn-group-vertical > .btn:active, +.btn-group > .btn.active, +.btn-group-vertical > .btn.active { + z-index: 2; +} +.btn-group .btn + .btn, +.btn-group .btn + .btn-group, +.btn-group .btn-group + .btn, +.btn-group .btn-group + .btn-group { + margin-left: -1px; +} +.btn-toolbar { + margin-left: -5px; +} +.btn-toolbar .btn-group, +.btn-toolbar .input-group { + float: left; +} +.btn-toolbar > .btn, +.btn-toolbar > .btn-group, +.btn-toolbar > .input-group { + margin-left: 5px; +} +.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { + border-radius: 0; +} +.btn-group > .btn:first-child { + margin-left: 0; +} +.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.btn-group > .btn:last-child:not(:first-child), +.btn-group > .dropdown-toggle:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group > .btn-group { + float: left; +} +.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group > .btn-group:first-child > .btn:last-child, +.btn-group > .btn-group:first-child > .dropdown-toggle { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.btn-group > .btn-group:last-child > .btn:first-child { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group .dropdown-toggle:active, +.btn-group.open .dropdown-toggle { + outline: 0; +} +.btn-group > .btn + .dropdown-toggle { + padding-right: 8px; + padding-left: 8px; +} +.btn-group > .btn-lg + .dropdown-toggle { + padding-right: 12px; + padding-left: 12px; +} +.btn-group.open .dropdown-toggle { + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); +} +.btn-group.open .dropdown-toggle.btn-link { + -webkit-box-shadow: none; + box-shadow: none; +} +.btn .caret { + margin-left: 0; +} +.btn-lg .caret { + border-width: 5px 5px 0; + border-bottom-width: 0; +} +.dropup .btn-lg .caret { + border-width: 0 5px 5px; +} +.btn-group-vertical > .btn, +.btn-group-vertical > .btn-group, +.btn-group-vertical > .btn-group > .btn { + display: block; + float: none; + width: 100%; + max-width: 100%; +} +.btn-group-vertical > .btn-group > .btn { + float: none; +} +.btn-group-vertical > .btn + .btn, +.btn-group-vertical > .btn + .btn-group, +.btn-group-vertical > .btn-group + .btn, +.btn-group-vertical > .btn-group + .btn-group { + margin-top: -1px; + margin-left: 0; +} +.btn-group-vertical > .btn:not(:first-child):not(:last-child) { + border-radius: 0; +} +.btn-group-vertical > .btn:first-child:not(:last-child) { + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn:last-child:not(:first-child) { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-left-radius: 4px; +} +.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { + border-radius: 0; +} +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, +.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.btn-group-justified { + display: table; + width: 100%; + table-layout: fixed; + border-collapse: separate; +} +.btn-group-justified > .btn, +.btn-group-justified > .btn-group { + display: table-cell; + float: none; + width: 1%; +} +.btn-group-justified > .btn-group .btn { + width: 100%; +} +.btn-group-justified > .btn-group .dropdown-menu { + left: auto; +} +[data-toggle="buttons"] > .btn input[type="radio"], +[data-toggle="buttons"] > .btn-group > .btn input[type="radio"], +[data-toggle="buttons"] > .btn input[type="checkbox"], +[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none; +} +.input-group { + position: relative; + display: table; + border-collapse: separate; +} +.input-group[class*="col-"] { + float: none; + padding-right: 0; + padding-left: 0; +} +.input-group .form-control { + position: relative; + z-index: 2; + float: left; + width: 100%; + margin-bottom: 0; +} +.input-group-lg > .form-control, +.input-group-lg > .input-group-addon, +.input-group-lg > .input-group-btn > .btn { + height: 46px; + padding: 10px 16px; + font-size: 18px; + line-height: 1.33; + border-radius: 6px; +} +select.input-group-lg > .form-control, +select.input-group-lg > .input-group-addon, +select.input-group-lg > .input-group-btn > .btn { + height: 46px; + line-height: 46px; +} +textarea.input-group-lg > .form-control, +textarea.input-group-lg > .input-group-addon, +textarea.input-group-lg > .input-group-btn > .btn, +select[multiple].input-group-lg > .form-control, +select[multiple].input-group-lg > .input-group-addon, +select[multiple].input-group-lg > .input-group-btn > .btn { + height: auto; +} +.input-group-sm > .form-control, +.input-group-sm > .input-group-addon, +.input-group-sm > .input-group-btn > .btn { + height: 30px; + padding: 5px 10px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} +select.input-group-sm > .form-control, +select.input-group-sm > .input-group-addon, +select.input-group-sm > .input-group-btn > .btn { + height: 30px; + line-height: 30px; +} +textarea.input-group-sm > .form-control, +textarea.input-group-sm > .input-group-addon, +textarea.input-group-sm > .input-group-btn > .btn, +select[multiple].input-group-sm > .form-control, +select[multiple].input-group-sm > .input-group-addon, +select[multiple].input-group-sm > .input-group-btn > .btn { + height: auto; +} +.input-group-addon, +.input-group-btn, +.input-group .form-control { + display: table-cell; +} +.input-group-addon:not(:first-child):not(:last-child), +.input-group-btn:not(:first-child):not(:last-child), +.input-group .form-control:not(:first-child):not(:last-child) { + border-radius: 0; +} +.input-group-addon, +.input-group-btn { + width: 1%; + white-space: nowrap; + vertical-align: middle; +} +.input-group-addon { + padding: 6px 12px; + font-size: 14px; + font-weight: normal; + line-height: 1; + color: #555; + text-align: center; + background-color: #eee; + border: 1px solid #ccc; + border-radius: 4px; +} +.input-group-addon.input-sm { + padding: 5px 10px; + font-size: 12px; + border-radius: 3px; +} +.input-group-addon.input-lg { + padding: 10px 16px; + font-size: 18px; + border-radius: 6px; +} +.input-group-addon input[type="radio"], +.input-group-addon input[type="checkbox"] { + margin-top: 0; +} +.input-group .form-control:first-child, +.input-group-addon:first-child, +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group > .btn, +.input-group-btn:first-child > .dropdown-toggle, +.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), +.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.input-group-addon:first-child { + border-right: 0; +} +.input-group .form-control:last-child, +.input-group-addon:last-child, +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group > .btn, +.input-group-btn:last-child > .dropdown-toggle, +.input-group-btn:first-child > .btn:not(:first-child), +.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.input-group-addon:last-child { + border-left: 0; +} +.input-group-btn { + position: relative; + font-size: 0; + white-space: nowrap; +} +.input-group-btn > .btn { + position: relative; +} +.input-group-btn > .btn + .btn { + margin-left: -1px; +} +.input-group-btn > .btn:hover, +.input-group-btn > .btn:focus, +.input-group-btn > .btn:active { + z-index: 2; +} +.input-group-btn:first-child > .btn, +.input-group-btn:first-child > .btn-group { + margin-right: -1px; +} +.input-group-btn:last-child > .btn, +.input-group-btn:last-child > .btn-group { + margin-left: -1px; +} +.nav { + padding-left: 0; + margin-bottom: 0; + list-style: none; +} +.nav > li { + position: relative; + display: block; +} +.nav > li > a { + position: relative; + display: block; + padding: 10px 15px; +} +.nav > li > a:hover, +.nav > li > a:focus { + text-decoration: none; + background-color: #eee; +} +.nav > li.disabled > a { + color: #777; +} +.nav > li.disabled > a:hover, +.nav > li.disabled > a:focus { + color: #777; + text-decoration: none; + cursor: not-allowed; + background-color: transparent; +} +.nav .open > a, +.nav .open > a:hover, +.nav .open > a:focus { + background-color: #eee; + border-color: #337ab7; +} +.nav .nav-divider { + height: 1px; + margin: 9px 0; + overflow: hidden; + background-color: #e5e5e5; +} +.nav > li > a > img { + max-width: none; +} +.nav-tabs { + border-bottom: 1px solid #ddd; +} +.nav-tabs > li { + float: left; + margin-bottom: -1px; +} +.nav-tabs > li > a { + margin-right: 2px; + line-height: 1.42857143; + border: 1px solid transparent; + border-radius: 4px 4px 0 0; +} +.nav-tabs > li > a:hover { + border-color: #eee #eee #ddd; +} +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:hover, +.nav-tabs > li.active > a:focus { + color: #555; + cursor: default; + background-color: #fff; + border: 1px solid #ddd; + border-bottom-color: transparent; +} +.nav-tabs.nav-justified { + width: 100%; + border-bottom: 0; +} +.nav-tabs.nav-justified > li { + float: none; +} +.nav-tabs.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} +.nav-tabs.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-tabs.nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs.nav-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs.nav-justified > .active > a, +.nav-tabs.nav-justified > .active > a:hover, +.nav-tabs.nav-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs.nav-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs.nav-justified > .active > a, + .nav-tabs.nav-justified > .active > a:hover, + .nav-tabs.nav-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.nav-pills > li { + float: left; +} +.nav-pills > li > a { + border-radius: 4px; +} +.nav-pills > li + li { + margin-left: 2px; +} +.nav-pills > li.active > a, +.nav-pills > li.active > a:hover, +.nav-pills > li.active > a:focus { + color: #fff; + background-color: #337ab7; +} +.nav-stacked > li { + float: none; +} +.nav-stacked > li + li { + margin-top: 2px; + margin-left: 0; +} +.nav-justified { + width: 100%; +} +.nav-justified > li { + float: none; +} +.nav-justified > li > a { + margin-bottom: 5px; + text-align: center; +} +.nav-justified > .dropdown .dropdown-menu { + top: auto; + left: auto; +} +@media (min-width: 768px) { + .nav-justified > li { + display: table-cell; + width: 1%; + } + .nav-justified > li > a { + margin-bottom: 0; + } +} +.nav-tabs-justified { + border-bottom: 0; +} +.nav-tabs-justified > li > a { + margin-right: 0; + border-radius: 4px; +} +.nav-tabs-justified > .active > a, +.nav-tabs-justified > .active > a:hover, +.nav-tabs-justified > .active > a:focus { + border: 1px solid #ddd; +} +@media (min-width: 768px) { + .nav-tabs-justified > li > a { + border-bottom: 1px solid #ddd; + border-radius: 4px 4px 0 0; + } + .nav-tabs-justified > .active > a, + .nav-tabs-justified > .active > a:hover, + .nav-tabs-justified > .active > a:focus { + border-bottom-color: #fff; + } +} +.tab-content > .tab-pane { + display: none; + visibility: hidden; +} +.tab-content > .active { + display: block; + visibility: visible; +} +.nav-tabs .dropdown-menu { + margin-top: -1px; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.navbar { + position: relative; + min-height: 50px; + margin-bottom: 20px; + border: 1px solid transparent; +} +@media (min-width: 768px) { + .navbar { + border-radius: 4px; + } +} +@media (min-width: 768px) { + .navbar-header { + float: left; + } +} +.navbar-collapse { + padding-right: 15px; + padding-left: 15px; + overflow-x: visible; + -webkit-overflow-scrolling: touch; + border-top: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); +} +.navbar-collapse.in { + overflow-y: auto; +} +@media (min-width: 768px) { + .navbar-collapse { + width: auto; + border-top: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-collapse.collapse { + display: block !important; + height: auto !important; + padding-bottom: 0; + overflow: visible !important; + visibility: visible !important; + } + .navbar-collapse.in { + overflow-y: visible; + } + .navbar-fixed-top .navbar-collapse, + .navbar-static-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + padding-right: 0; + padding-left: 0; + } +} +.navbar-fixed-top .navbar-collapse, +.navbar-fixed-bottom .navbar-collapse { + max-height: 340px; +} +@media (max-device-width: 480px) and (orientation: landscape) { + .navbar-fixed-top .navbar-collapse, + .navbar-fixed-bottom .navbar-collapse { + max-height: 200px; + } +} +.container > .navbar-header, +.container-fluid > .navbar-header, +.container > .navbar-collapse, +.container-fluid > .navbar-collapse { + margin-right: -15px; + margin-left: -15px; +} +@media (min-width: 768px) { + .container > .navbar-header, + .container-fluid > .navbar-header, + .container > .navbar-collapse, + .container-fluid > .navbar-collapse { + margin-right: 0; + margin-left: 0; + } +} +.navbar-static-top { + z-index: 1000; + border-width: 0 0 1px; +} +@media (min-width: 768px) { + .navbar-static-top { + border-radius: 0; + } +} +.navbar-fixed-top, +.navbar-fixed-bottom { + position: fixed; + right: 0; + left: 0; + z-index: 1030; +} +@media (min-width: 768px) { + .navbar-fixed-top, + .navbar-fixed-bottom { + border-radius: 0; + } +} +.navbar-fixed-top { + top: 0; + border-width: 0 0 1px; +} +.navbar-fixed-bottom { + bottom: 0; + margin-bottom: 0; + border-width: 1px 0 0; +} +.navbar-brand { + float: left; + height: 50px; + padding: 15px 15px; + font-size: 18px; + line-height: 20px; +} +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} +.navbar-brand > img { + display: block; +} +@media (min-width: 768px) { + .navbar > .container .navbar-brand, + .navbar > .container-fluid .navbar-brand { + margin-left: -15px; + } +} +.navbar-toggle { + position: relative; + float: right; + padding: 9px 10px; + margin-top: 8px; + margin-right: 15px; + margin-bottom: 8px; + background-color: transparent; + background-image: none; + border: 1px solid transparent; + border-radius: 4px; +} +.navbar-toggle:focus { + outline: 0; +} +.navbar-toggle .icon-bar { + display: block; + width: 22px; + height: 2px; + border-radius: 1px; +} +.navbar-toggle .icon-bar + .icon-bar { + margin-top: 4px; +} +@media (min-width: 768px) { + .navbar-toggle { + display: none; + } +} +.navbar-nav { + margin: 7.5px -15px; +} +.navbar-nav > li > a { + padding-top: 10px; + padding-bottom: 10px; + line-height: 20px; +} +@media (max-width: 767px) { + .navbar-nav .open .dropdown-menu { + position: static; + float: none; + width: auto; + margin-top: 0; + background-color: transparent; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } + .navbar-nav .open .dropdown-menu > li > a, + .navbar-nav .open .dropdown-menu .dropdown-header { + padding: 5px 15px 5px 25px; + } + .navbar-nav .open .dropdown-menu > li > a { + line-height: 20px; + } + .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-nav .open .dropdown-menu > li > a:focus { + background-image: none; + } +} +@media (min-width: 768px) { + .navbar-nav { + float: left; + margin: 0; + } + .navbar-nav > li { + float: left; + } + .navbar-nav > li > a { + padding-top: 15px; + padding-bottom: 15px; + } +} +.navbar-form { + padding: 10px 15px; + margin-top: 8px; + margin-right: -15px; + margin-bottom: 8px; + margin-left: -15px; + border-top: 1px solid transparent; + border-bottom: 1px solid transparent; + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); +} +@media (min-width: 768px) { + .navbar-form .form-group { + display: inline-block; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .navbar-form .form-control-static { + display: inline-block; + } + .navbar-form .input-group { + display: inline-table; + vertical-align: middle; + } + .navbar-form .input-group .input-group-addon, + .navbar-form .input-group .input-group-btn, + .navbar-form .input-group .form-control { + width: auto; + } + .navbar-form .input-group > .form-control { + width: 100%; + } + .navbar-form .control-label { + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio, + .navbar-form .checkbox { + display: inline-block; + margin-top: 0; + margin-bottom: 0; + vertical-align: middle; + } + .navbar-form .radio label, + .navbar-form .checkbox label { + padding-left: 0; + } + .navbar-form .radio input[type="radio"], + .navbar-form .checkbox input[type="checkbox"] { + position: relative; + margin-left: 0; + } + .navbar-form .has-feedback .form-control-feedback { + top: 0; + } +} +@media (max-width: 767px) { + .navbar-form .form-group { + margin-bottom: 5px; + } + .navbar-form .form-group:last-child { + margin-bottom: 0; + } +} +@media (min-width: 768px) { + .navbar-form { + width: auto; + padding-top: 0; + padding-bottom: 0; + margin-right: 0; + margin-left: 0; + border: 0; + -webkit-box-shadow: none; + box-shadow: none; + } +} +.navbar-nav > li > .dropdown-menu { + margin-top: 0; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.navbar-btn { + margin-top: 8px; + margin-bottom: 8px; +} +.navbar-btn.btn-sm { + margin-top: 10px; + margin-bottom: 10px; +} +.navbar-btn.btn-xs { + margin-top: 14px; + margin-bottom: 14px; +} +.navbar-text { + margin-top: 15px; + margin-bottom: 15px; +} +@media (min-width: 768px) { + .navbar-text { + float: left; + margin-right: 15px; + margin-left: 15px; + } +} +@media (min-width: 768px) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + margin-right: -15px; + } + .navbar-right ~ .navbar-right { + margin-right: 0; + } +} +.navbar-default { + background-color: #f8f8f8; + border-color: #e7e7e7; +} +.navbar-default .navbar-brand { + color: #777; +} +.navbar-default .navbar-brand:hover, +.navbar-default .navbar-brand:focus { + color: #5e5e5e; + background-color: transparent; +} +.navbar-default .navbar-text { + color: #777; +} +.navbar-default .navbar-nav > li > a { + color: #777; +} +.navbar-default .navbar-nav > li > a:hover, +.navbar-default .navbar-nav > li > a:focus { + color: #333; + background-color: transparent; +} +.navbar-default .navbar-nav > .active > a, +.navbar-default .navbar-nav > .active > a:hover, +.navbar-default .navbar-nav > .active > a:focus { + color: #555; + background-color: #e7e7e7; +} +.navbar-default .navbar-nav > .disabled > a, +.navbar-default .navbar-nav > .disabled > a:hover, +.navbar-default .navbar-nav > .disabled > a:focus { + color: #ccc; + background-color: transparent; +} +.navbar-default .navbar-toggle { + border-color: #ddd; +} +.navbar-default .navbar-toggle:hover, +.navbar-default .navbar-toggle:focus { + background-color: #ddd; +} +.navbar-default .navbar-toggle .icon-bar { + background-color: #888; +} +.navbar-default .navbar-collapse, +.navbar-default .navbar-form { + border-color: #e7e7e7; +} +.navbar-default .navbar-nav > .open > a, +.navbar-default .navbar-nav > .open > a:hover, +.navbar-default .navbar-nav > .open > a:focus { + color: #555; + background-color: #e7e7e7; +} +@media (max-width: 767px) { + .navbar-default .navbar-nav .open .dropdown-menu > li > a { + color: #777; + } + .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { + color: #333; + background-color: transparent; + } + .navbar-default .navbar-nav .open .dropdown-menu > .active > a, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #555; + background-color: #e7e7e7; + } + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #ccc; + background-color: transparent; + } +} +.navbar-default .navbar-link { + color: #777; +} +.navbar-default .navbar-link:hover { + color: #333; +} +.navbar-default .btn-link { + color: #777; +} +.navbar-default .btn-link:hover, +.navbar-default .btn-link:focus { + color: #333; +} +.navbar-default .btn-link[disabled]:hover, +fieldset[disabled] .navbar-default .btn-link:hover, +.navbar-default .btn-link[disabled]:focus, +fieldset[disabled] .navbar-default .btn-link:focus { + color: #ccc; +} +.navbar-inverse { + background-color: #222; + border-color: #080808; +} +.navbar-inverse .navbar-brand { + color: #9d9d9d; +} +.navbar-inverse .navbar-brand:hover, +.navbar-inverse .navbar-brand:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-text { + color: #9d9d9d; +} +.navbar-inverse .navbar-nav > li > a { + color: #9d9d9d; +} +.navbar-inverse .navbar-nav > li > a:hover, +.navbar-inverse .navbar-nav > li > a:focus { + color: #fff; + background-color: transparent; +} +.navbar-inverse .navbar-nav > .active > a, +.navbar-inverse .navbar-nav > .active > a:hover, +.navbar-inverse .navbar-nav > .active > a:focus { + color: #fff; + background-color: #080808; +} +.navbar-inverse .navbar-nav > .disabled > a, +.navbar-inverse .navbar-nav > .disabled > a:hover, +.navbar-inverse .navbar-nav > .disabled > a:focus { + color: #444; + background-color: transparent; +} +.navbar-inverse .navbar-toggle { + border-color: #333; +} +.navbar-inverse .navbar-toggle:hover, +.navbar-inverse .navbar-toggle:focus { + background-color: #333; +} +.navbar-inverse .navbar-toggle .icon-bar { + background-color: #fff; +} +.navbar-inverse .navbar-collapse, +.navbar-inverse .navbar-form { + border-color: #101010; +} +.navbar-inverse .navbar-nav > .open > a, +.navbar-inverse .navbar-nav > .open > a:hover, +.navbar-inverse .navbar-nav > .open > a:focus { + color: #fff; + background-color: #080808; +} +@media (max-width: 767px) { + .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { + border-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu .divider { + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { + color: #9d9d9d; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { + color: #fff; + background-color: transparent; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { + color: #fff; + background-color: #080808; + } + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, + .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { + color: #444; + background-color: transparent; + } +} +.navbar-inverse .navbar-link { + color: #9d9d9d; +} +.navbar-inverse .navbar-link:hover { + color: #fff; +} +.navbar-inverse .btn-link { + color: #9d9d9d; +} +.navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link:focus { + color: #fff; +} +.navbar-inverse .btn-link[disabled]:hover, +fieldset[disabled] .navbar-inverse .btn-link:hover, +.navbar-inverse .btn-link[disabled]:focus, +fieldset[disabled] .navbar-inverse .btn-link:focus { + color: #444; +} +.breadcrumb { + padding: 8px 15px; + margin-bottom: 20px; + list-style: none; + background-color: #f5f5f5; + border-radius: 4px; +} +.breadcrumb > li { + display: inline-block; +} +.breadcrumb > li + li:before { + padding: 0 5px; + color: #ccc; + content: "/\00a0"; +} +.breadcrumb > .active { + color: #777; +} +.pagination { + display: inline-block; + padding-left: 0; + margin: 20px 0; + border-radius: 4px; +} +.pagination > li { + display: inline; +} +.pagination > li > a, +.pagination > li > span { + position: relative; + float: left; + padding: 6px 12px; + margin-left: -1px; + line-height: 1.42857143; + color: #337ab7; + text-decoration: none; + background-color: #fff; + border: 1px solid #ddd; +} +.pagination > li:first-child > a, +.pagination > li:first-child > span { + margin-left: 0; + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} +.pagination > li:last-child > a, +.pagination > li:last-child > span { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} +.pagination > li > a:hover, +.pagination > li > span:hover, +.pagination > li > a:focus, +.pagination > li > span:focus { + color: #23527c; + background-color: #eee; + border-color: #ddd; +} +.pagination > .active > a, +.pagination > .active > span, +.pagination > .active > a:hover, +.pagination > .active > span:hover, +.pagination > .active > a:focus, +.pagination > .active > span:focus { + z-index: 2; + color: #fff; + cursor: default; + background-color: #337ab7; + border-color: #337ab7; +} +.pagination > .disabled > span, +.pagination > .disabled > span:hover, +.pagination > .disabled > span:focus, +.pagination > .disabled > a, +.pagination > .disabled > a:hover, +.pagination > .disabled > a:focus { + color: #777; + cursor: not-allowed; + background-color: #fff; + border-color: #ddd; +} +.pagination-lg > li > a, +.pagination-lg > li > span { + padding: 10px 16px; + font-size: 18px; +} +.pagination-lg > li:first-child > a, +.pagination-lg > li:first-child > span { + border-top-left-radius: 6px; + border-bottom-left-radius: 6px; +} +.pagination-lg > li:last-child > a, +.pagination-lg > li:last-child > span { + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; +} +.pagination-sm > li > a, +.pagination-sm > li > span { + padding: 5px 10px; + font-size: 12px; +} +.pagination-sm > li:first-child > a, +.pagination-sm > li:first-child > span { + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; +} +.pagination-sm > li:last-child > a, +.pagination-sm > li:last-child > span { + border-top-right-radius: 3px; + border-bottom-right-radius: 3px; +} +.pager { + padding-left: 0; + margin: 20px 0; + text-align: center; + list-style: none; +} +.pager li { + display: inline; +} +.pager li > a, +.pager li > span { + display: inline-block; + padding: 5px 14px; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 15px; +} +.pager li > a:hover, +.pager li > a:focus { + text-decoration: none; + background-color: #eee; +} +.pager .next > a, +.pager .next > span { + float: right; +} +.pager .previous > a, +.pager .previous > span { + float: left; +} +.pager .disabled > a, +.pager .disabled > a:hover, +.pager .disabled > a:focus, +.pager .disabled > span { + color: #777; + cursor: not-allowed; + background-color: #fff; +} +.label { + display: inline; + padding: .2em .6em .3em; + font-size: 75%; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: .25em; +} +a.label:hover, +a.label:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +.label:empty { + display: none; +} +.btn .label { + position: relative; + top: -1px; +} +.label-default { + background-color: #777; +} +.label-default[href]:hover, +.label-default[href]:focus { + background-color: #5e5e5e; +} +.label-primary { + background-color: #337ab7; +} +.label-primary[href]:hover, +.label-primary[href]:focus { + background-color: #286090; +} +.label-success { + background-color: #5cb85c; +} +.label-success[href]:hover, +.label-success[href]:focus { + background-color: #449d44; +} +.label-info { + background-color: #5bc0de; +} +.label-info[href]:hover, +.label-info[href]:focus { + background-color: #31b0d5; +} +.label-warning { + background-color: #f0ad4e; +} +.label-warning[href]:hover, +.label-warning[href]:focus { + background-color: #ec971f; +} +.label-danger { + background-color: #d9534f; +} +.label-danger[href]:hover, +.label-danger[href]:focus { + background-color: #c9302c; +} +.badge { + display: inline-block; + min-width: 10px; + padding: 3px 7px; + font-size: 12px; + font-weight: bold; + line-height: 1; + color: #fff; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + background-color: #777; + border-radius: 10px; +} +.badge:empty { + display: none; +} +.btn .badge { + position: relative; + top: -1px; +} +.btn-xs .badge { + top: 0; + padding: 1px 5px; +} +a.badge:hover, +a.badge:focus { + color: #fff; + text-decoration: none; + cursor: pointer; +} +.list-group-item.active > .badge, +.nav-pills > .active > a > .badge { + color: #337ab7; + background-color: #fff; +} +.list-group-item > .badge { + float: right; +} +.list-group-item > .badge + .badge { + margin-right: 5px; +} +.nav-pills > li > a > .badge { + margin-left: 3px; +} +.jumbotron { + padding: 30px 15px; + margin-bottom: 30px; + color: inherit; + background-color: #eee; +} +.jumbotron h1, +.jumbotron .h1 { + color: inherit; +} +.jumbotron p { + margin-bottom: 15px; + font-size: 21px; + font-weight: 200; +} +.jumbotron > hr { + border-top-color: #d5d5d5; +} +.container .jumbotron, +.container-fluid .jumbotron { + border-radius: 6px; +} +.jumbotron .container { + max-width: 100%; +} +@media screen and (min-width: 768px) { + .jumbotron { + padding: 48px 0; + } + .container .jumbotron, + .container-fluid .jumbotron { + padding-right: 60px; + padding-left: 60px; + } + .jumbotron h1, + .jumbotron .h1 { + font-size: 63px; + } +} +.thumbnail { + display: block; + padding: 4px; + margin-bottom: 20px; + line-height: 1.42857143; + background-color: #fff; + border: 1px solid #ddd; + border-radius: 4px; + -webkit-transition: border .2s ease-in-out; + -o-transition: border .2s ease-in-out; + transition: border .2s ease-in-out; +} +.thumbnail > img, +.thumbnail a > img { + margin-right: auto; + margin-left: auto; +} +a.thumbnail:hover, +a.thumbnail:focus, +a.thumbnail.active { + border-color: #337ab7; +} +.thumbnail .caption { + padding: 9px; + color: #333; +} +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; +} +.alert h4 { + margin-top: 0; + color: inherit; +} +.alert .alert-link { + font-weight: bold; +} +.alert > p, +.alert > ul { + margin-bottom: 0; +} +.alert > p + p { + margin-top: 5px; +} +.alert-dismissable, +.alert-dismissible { + padding-right: 35px; +} +.alert-dismissable .close, +.alert-dismissible .close { + position: relative; + top: -2px; + right: -21px; + color: inherit; +} +.alert-success { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.alert-success hr { + border-top-color: #c9e2b3; +} +.alert-success .alert-link { + color: #2b542c; +} +.alert-info { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.alert-info hr { + border-top-color: #a6e1ec; +} +.alert-info .alert-link { + color: #245269; +} +.alert-warning { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.alert-warning hr { + border-top-color: #f7e1b5; +} +.alert-warning .alert-link { + color: #66512c; +} +.alert-danger { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.alert-danger hr { + border-top-color: #e4b9c0; +} +.alert-danger .alert-link { + color: #843534; +} +@-webkit-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@-o-keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +@keyframes progress-bar-stripes { + from { + background-position: 40px 0; + } + to { + background-position: 0 0; + } +} +.progress { + height: 20px; + margin-bottom: 20px; + overflow: hidden; + background-color: #f5f5f5; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); +} +.progress-bar { + float: left; + width: 0; + height: 100%; + font-size: 12px; + line-height: 20px; + color: #fff; + text-align: center; + background-color: #337ab7; + -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); + -webkit-transition: width .6s ease; + -o-transition: width .6s ease; + transition: width .6s ease; +} +.progress-striped .progress-bar, +.progress-bar-striped { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + -webkit-background-size: 40px 40px; + background-size: 40px 40px; +} +.progress.active .progress-bar, +.progress-bar.active { + -webkit-animation: progress-bar-stripes 2s linear infinite; + -o-animation: progress-bar-stripes 2s linear infinite; + animation: progress-bar-stripes 2s linear infinite; +} +.progress-bar-success { + background-color: #5cb85c; +} +.progress-striped .progress-bar-success { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-info { + background-color: #5bc0de; +} +.progress-striped .progress-bar-info { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-warning { + background-color: #f0ad4e; +} +.progress-striped .progress-bar-warning { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.progress-bar-danger { + background-color: #d9534f; +} +.progress-striped .progress-bar-danger { + background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); + background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); +} +.media { + margin-top: 15px; +} +.media:first-child { + margin-top: 0; +} +.media-right, +.media > .pull-right { + padding-left: 10px; +} +.media-left, +.media > .pull-left { + padding-right: 10px; +} +.media-left, +.media-right, +.media-body { + display: table-cell; + vertical-align: top; +} +.media-middle { + vertical-align: middle; +} +.media-bottom { + vertical-align: bottom; +} +.media-heading { + margin-top: 0; + margin-bottom: 5px; +} +.media-list { + padding-left: 0; + list-style: none; +} +.list-group { + padding-left: 0; + margin-bottom: 20px; +} +.list-group-item { + position: relative; + display: block; + padding: 10px 15px; + margin-bottom: -1px; + background-color: #fff; + border: 1px solid #ddd; +} +.list-group-item:first-child { + border-top-left-radius: 4px; + border-top-right-radius: 4px; +} +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 4px; + border-bottom-left-radius: 4px; +} +a.list-group-item { + color: #555; +} +a.list-group-item .list-group-item-heading { + color: #333; +} +a.list-group-item:hover, +a.list-group-item:focus { + color: #555; + text-decoration: none; + background-color: #f5f5f5; +} +.list-group-item.disabled, +.list-group-item.disabled:hover, +.list-group-item.disabled:focus { + color: #777; + cursor: not-allowed; + background-color: #eee; +} +.list-group-item.disabled .list-group-item-heading, +.list-group-item.disabled:hover .list-group-item-heading, +.list-group-item.disabled:focus .list-group-item-heading { + color: inherit; +} +.list-group-item.disabled .list-group-item-text, +.list-group-item.disabled:hover .list-group-item-text, +.list-group-item.disabled:focus .list-group-item-text { + color: #777; +} +.list-group-item.active, +.list-group-item.active:hover, +.list-group-item.active:focus { + z-index: 2; + color: #fff; + background-color: #337ab7; + border-color: #337ab7; +} +.list-group-item.active .list-group-item-heading, +.list-group-item.active:hover .list-group-item-heading, +.list-group-item.active:focus .list-group-item-heading, +.list-group-item.active .list-group-item-heading > small, +.list-group-item.active:hover .list-group-item-heading > small, +.list-group-item.active:focus .list-group-item-heading > small, +.list-group-item.active .list-group-item-heading > .small, +.list-group-item.active:hover .list-group-item-heading > .small, +.list-group-item.active:focus .list-group-item-heading > .small { + color: inherit; +} +.list-group-item.active .list-group-item-text, +.list-group-item.active:hover .list-group-item-text, +.list-group-item.active:focus .list-group-item-text { + color: #c7ddef; +} +.list-group-item-success { + color: #3c763d; + background-color: #dff0d8; +} +a.list-group-item-success { + color: #3c763d; +} +a.list-group-item-success .list-group-item-heading { + color: inherit; +} +a.list-group-item-success:hover, +a.list-group-item-success:focus { + color: #3c763d; + background-color: #d0e9c6; +} +a.list-group-item-success.active, +a.list-group-item-success.active:hover, +a.list-group-item-success.active:focus { + color: #fff; + background-color: #3c763d; + border-color: #3c763d; +} +.list-group-item-info { + color: #31708f; + background-color: #d9edf7; +} +a.list-group-item-info { + color: #31708f; +} +a.list-group-item-info .list-group-item-heading { + color: inherit; +} +a.list-group-item-info:hover, +a.list-group-item-info:focus { + color: #31708f; + background-color: #c4e3f3; +} +a.list-group-item-info.active, +a.list-group-item-info.active:hover, +a.list-group-item-info.active:focus { + color: #fff; + background-color: #31708f; + border-color: #31708f; +} +.list-group-item-warning { + color: #8a6d3b; + background-color: #fcf8e3; +} +a.list-group-item-warning { + color: #8a6d3b; +} +a.list-group-item-warning .list-group-item-heading { + color: inherit; +} +a.list-group-item-warning:hover, +a.list-group-item-warning:focus { + color: #8a6d3b; + background-color: #faf2cc; +} +a.list-group-item-warning.active, +a.list-group-item-warning.active:hover, +a.list-group-item-warning.active:focus { + color: #fff; + background-color: #8a6d3b; + border-color: #8a6d3b; +} +.list-group-item-danger { + color: #a94442; + background-color: #f2dede; +} +a.list-group-item-danger { + color: #a94442; +} +a.list-group-item-danger .list-group-item-heading { + color: inherit; +} +a.list-group-item-danger:hover, +a.list-group-item-danger:focus { + color: #a94442; + background-color: #ebcccc; +} +a.list-group-item-danger.active, +a.list-group-item-danger.active:hover, +a.list-group-item-danger.active:focus { + color: #fff; + background-color: #a94442; + border-color: #a94442; +} +.list-group-item-heading { + margin-top: 0; + margin-bottom: 5px; +} +.list-group-item-text { + margin-bottom: 0; + line-height: 1.3; +} +.panel { + margin-bottom: 20px; + background-color: #fff; + border: 1px solid transparent; + border-radius: 4px; + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: 0 1px 1px rgba(0, 0, 0, .05); +} +.panel-body { + padding: 15px; +} +.panel-heading { + padding: 10px 15px; + border-bottom: 1px solid transparent; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel-heading > .dropdown .dropdown-toggle { + color: inherit; +} +.panel-title { + margin-top: 0; + margin-bottom: 0; + font-size: 16px; + color: inherit; +} +.panel-title > a { + color: inherit; +} +.panel-footer { + padding: 10px 15px; + background-color: #f5f5f5; + border-top: 1px solid #ddd; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .list-group, +.panel > .panel-collapse > .list-group { + margin-bottom: 0; +} +.panel > .list-group .list-group-item, +.panel > .panel-collapse > .list-group .list-group-item { + border-width: 1px 0; + border-radius: 0; +} +.panel > .list-group:first-child .list-group-item:first-child, +.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { + border-top: 0; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .list-group:last-child .list-group-item:last-child, +.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { + border-bottom: 0; + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel-heading + .list-group .list-group-item:first-child { + border-top-width: 0; +} +.list-group + .panel-footer { + border-top-width: 0; +} +.panel > .table, +.panel > .table-responsive > .table, +.panel > .panel-collapse > .table { + margin-bottom: 0; +} +.panel > .table caption, +.panel > .table-responsive > .table caption, +.panel > .panel-collapse > .table caption { + padding-right: 15px; + padding-left: 15px; +} +.panel > .table:first-child, +.panel > .table-responsive:first-child > .table:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { + border-top-left-radius: 3px; +} +.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, +.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, +.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, +.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { + border-top-right-radius: 3px; +} +.panel > .table:last-child, +.panel > .table-responsive:last-child > .table:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { + border-bottom-right-radius: 3px; + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { + border-bottom-left-radius: 3px; +} +.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, +.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, +.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, +.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { + border-bottom-right-radius: 3px; +} +.panel > .panel-body + .table, +.panel > .panel-body + .table-responsive, +.panel > .table + .panel-body, +.panel > .table-responsive + .panel-body { + border-top: 1px solid #ddd; +} +.panel > .table > tbody:first-child > tr:first-child th, +.panel > .table > tbody:first-child > tr:first-child td { + border-top: 0; +} +.panel > .table-bordered, +.panel > .table-responsive > .table-bordered { + border: 0; +} +.panel > .table-bordered > thead > tr > th:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, +.panel > .table-bordered > tbody > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, +.panel > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, +.panel > .table-bordered > thead > tr > td:first-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, +.panel > .table-bordered > tbody > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, +.panel > .table-bordered > tfoot > tr > td:first-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { + border-left: 0; +} +.panel > .table-bordered > thead > tr > th:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, +.panel > .table-bordered > tbody > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, +.panel > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, +.panel > .table-bordered > thead > tr > td:last-child, +.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, +.panel > .table-bordered > tbody > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, +.panel > .table-bordered > tfoot > tr > td:last-child, +.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { + border-right: 0; +} +.panel > .table-bordered > thead > tr:first-child > td, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, +.panel > .table-bordered > tbody > tr:first-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, +.panel > .table-bordered > thead > tr:first-child > th, +.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, +.panel > .table-bordered > tbody > tr:first-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { + border-bottom: 0; +} +.panel > .table-bordered > tbody > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, +.panel > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, +.panel > .table-bordered > tbody > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, +.panel > .table-bordered > tfoot > tr:last-child > th, +.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { + border-bottom: 0; +} +.panel > .table-responsive { + margin-bottom: 0; + border: 0; +} +.panel-group { + margin-bottom: 20px; +} +.panel-group .panel { + margin-bottom: 0; + border-radius: 4px; +} +.panel-group .panel + .panel { + margin-top: 5px; +} +.panel-group .panel-heading { + border-bottom: 0; +} +.panel-group .panel-heading + .panel-collapse > .panel-body, +.panel-group .panel-heading + .panel-collapse > .list-group { + border-top: 1px solid #ddd; +} +.panel-group .panel-footer { + border-top: 0; +} +.panel-group .panel-footer + .panel-collapse .panel-body { + border-bottom: 1px solid #ddd; +} +.panel-default { + border-color: #ddd; +} +.panel-default > .panel-heading { + color: #333; + background-color: #f5f5f5; + border-color: #ddd; +} +.panel-default > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ddd; +} +.panel-default > .panel-heading .badge { + color: #f5f5f5; + background-color: #333; +} +.panel-default > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ddd; +} +.panel-primary { + border-color: #337ab7; +} +.panel-primary > .panel-heading { + color: #fff; + background-color: #337ab7; + border-color: #337ab7; +} +.panel-primary > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #337ab7; +} +.panel-primary > .panel-heading .badge { + color: #337ab7; + background-color: #fff; +} +.panel-primary > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #337ab7; +} +.panel-success { + border-color: #d6e9c6; +} +.panel-success > .panel-heading { + color: #3c763d; + background-color: #dff0d8; + border-color: #d6e9c6; +} +.panel-success > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #d6e9c6; +} +.panel-success > .panel-heading .badge { + color: #dff0d8; + background-color: #3c763d; +} +.panel-success > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #d6e9c6; +} +.panel-info { + border-color: #bce8f1; +} +.panel-info > .panel-heading { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.panel-info > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #bce8f1; +} +.panel-info > .panel-heading .badge { + color: #d9edf7; + background-color: #31708f; +} +.panel-info > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #bce8f1; +} +.panel-warning { + border-color: #faebcc; +} +.panel-warning > .panel-heading { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.panel-warning > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #faebcc; +} +.panel-warning > .panel-heading .badge { + color: #fcf8e3; + background-color: #8a6d3b; +} +.panel-warning > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #faebcc; +} +.panel-danger { + border-color: #ebccd1; +} +.panel-danger > .panel-heading { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.panel-danger > .panel-heading + .panel-collapse > .panel-body { + border-top-color: #ebccd1; +} +.panel-danger > .panel-heading .badge { + color: #f2dede; + background-color: #a94442; +} +.panel-danger > .panel-footer + .panel-collapse > .panel-body { + border-bottom-color: #ebccd1; +} +.embed-responsive { + position: relative; + display: block; + height: 0; + padding: 0; + overflow: hidden; +} +.embed-responsive .embed-responsive-item, +.embed-responsive iframe, +.embed-responsive embed, +.embed-responsive object, +.embed-responsive video { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + border: 0; +} +.embed-responsive.embed-responsive-16by9 { + padding-bottom: 56.25%; +} +.embed-responsive.embed-responsive-4by3 { + padding-bottom: 75%; +} +.well { + min-height: 20px; + padding: 19px; + margin-bottom: 20px; + background-color: #f5f5f5; + border: 1px solid #e3e3e3; + border-radius: 4px; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); +} +.well blockquote { + border-color: #ddd; + border-color: rgba(0, 0, 0, .15); +} +.well-lg { + padding: 24px; + border-radius: 6px; +} +.well-sm { + padding: 9px; + border-radius: 3px; +} +.close { + float: right; + font-size: 21px; + font-weight: bold; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + filter: alpha(opacity=20); + opacity: .2; +} +.close:hover, +.close:focus { + color: #000; + text-decoration: none; + cursor: pointer; + filter: alpha(opacity=50); + opacity: .5; +} +button.close { + -webkit-appearance: none; + padding: 0; + cursor: pointer; + background: transparent; + border: 0; +} +.modal-open { + overflow: hidden; +} +.modal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + display: none; + overflow: hidden; + -webkit-overflow-scrolling: touch; + outline: 0; +} +.modal.fade .modal-dialog { + -webkit-transition: -webkit-transform .3s ease-out; + -o-transition: -o-transform .3s ease-out; + transition: transform .3s ease-out; + -webkit-transform: translate(0, -25%); + -ms-transform: translate(0, -25%); + -o-transform: translate(0, -25%); + transform: translate(0, -25%); +} +.modal.in .modal-dialog { + -webkit-transform: translate(0, 0); + -ms-transform: translate(0, 0); + -o-transform: translate(0, 0); + transform: translate(0, 0); +} +.modal-open .modal { + overflow-x: hidden; + overflow-y: auto; +} +.modal-dialog { + position: relative; + width: auto; + margin: 10px; +} +.modal-content { + position: relative; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #999; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + outline: 0; + -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); + box-shadow: 0 3px 9px rgba(0, 0, 0, .5); +} +.modal-backdrop { + position: absolute; + top: 0; + right: 0; + left: 0; + background-color: #000; +} +.modal-backdrop.fade { + filter: alpha(opacity=0); + opacity: 0; +} +.modal-backdrop.in { + filter: alpha(opacity=50); + opacity: .5; +} +.modal-header { + min-height: 16.42857143px; + padding: 15px; + border-bottom: 1px solid #e5e5e5; +} +.modal-header .close { + margin-top: -2px; +} +.modal-title { + margin: 0; + line-height: 1.42857143; +} +.modal-body { + position: relative; + padding: 15px; +} +.modal-footer { + padding: 15px; + text-align: right; + border-top: 1px solid #e5e5e5; +} +.modal-footer .btn + .btn { + margin-bottom: 0; + margin-left: 5px; +} +.modal-footer .btn-group .btn + .btn { + margin-left: -1px; +} +.modal-footer .btn-block + .btn-block { + margin-left: 0; +} +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; +} +@media (min-width: 768px) { + .modal-dialog { + width: 600px; + margin: 30px auto; + } + .modal-content { + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + box-shadow: 0 5px 15px rgba(0, 0, 0, .5); + } + .modal-sm { + width: 300px; + } +} +@media (min-width: 992px) { + .modal-lg { + width: 900px; + } +} +.tooltip { + position: absolute; + z-index: 1070; + display: block; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 12px; + font-weight: normal; + line-height: 1.4; + visibility: visible; + filter: alpha(opacity=0); + opacity: 0; +} +.tooltip.in { + filter: alpha(opacity=90); + opacity: .9; +} +.tooltip.top { + padding: 5px 0; + margin-top: -3px; +} +.tooltip.right { + padding: 0 5px; + margin-left: 3px; +} +.tooltip.bottom { + padding: 5px 0; + margin-top: 3px; +} +.tooltip.left { + padding: 0 5px; + margin-left: -3px; +} +.tooltip-inner { + max-width: 200px; + padding: 3px 8px; + color: #fff; + text-align: center; + text-decoration: none; + background-color: #000; + border-radius: 4px; +} +.tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.tooltip.top .tooltip-arrow { + bottom: 0; + left: 50%; + margin-left: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-left .tooltip-arrow { + right: 5px; + bottom: 0; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.top-right .tooltip-arrow { + bottom: 0; + left: 5px; + margin-bottom: -5px; + border-width: 5px 5px 0; + border-top-color: #000; +} +.tooltip.right .tooltip-arrow { + top: 50%; + left: 0; + margin-top: -5px; + border-width: 5px 5px 5px 0; + border-right-color: #000; +} +.tooltip.left .tooltip-arrow { + top: 50%; + right: 0; + margin-top: -5px; + border-width: 5px 0 5px 5px; + border-left-color: #000; +} +.tooltip.bottom .tooltip-arrow { + top: 0; + left: 50%; + margin-left: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-left .tooltip-arrow { + top: 0; + right: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.tooltip.bottom-right .tooltip-arrow { + top: 0; + left: 5px; + margin-top: -5px; + border-width: 0 5px 5px; + border-bottom-color: #000; +} +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1060; + display: none; + max-width: 276px; + padding: 1px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 14px; + font-weight: normal; + line-height: 1.42857143; + text-align: left; + white-space: normal; + background-color: #fff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #ccc; + border: 1px solid rgba(0, 0, 0, .2); + border-radius: 6px; + -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); + box-shadow: 0 5px 10px rgba(0, 0, 0, .2); +} +.popover.top { + margin-top: -10px; +} +.popover.right { + margin-left: 10px; +} +.popover.bottom { + margin-top: 10px; +} +.popover.left { + margin-left: -10px; +} +.popover-title { + padding: 8px 14px; + margin: 0; + font-size: 14px; + background-color: #f7f7f7; + border-bottom: 1px solid #ebebeb; + border-radius: 5px 5px 0 0; +} +.popover-content { + padding: 9px 14px; +} +.popover > .arrow, +.popover > .arrow:after { + position: absolute; + display: block; + width: 0; + height: 0; + border-color: transparent; + border-style: solid; +} +.popover > .arrow { + border-width: 11px; +} +.popover > .arrow:after { + content: ""; + border-width: 10px; +} +.popover.top > .arrow { + bottom: -11px; + left: 50%; + margin-left: -11px; + border-top-color: #999; + border-top-color: rgba(0, 0, 0, .25); + border-bottom-width: 0; +} +.popover.top > .arrow:after { + bottom: 1px; + margin-left: -10px; + content: " "; + border-top-color: #fff; + border-bottom-width: 0; +} +.popover.right > .arrow { + top: 50%; + left: -11px; + margin-top: -11px; + border-right-color: #999; + border-right-color: rgba(0, 0, 0, .25); + border-left-width: 0; +} +.popover.right > .arrow:after { + bottom: -10px; + left: 1px; + content: " "; + border-right-color: #fff; + border-left-width: 0; +} +.popover.bottom > .arrow { + top: -11px; + left: 50%; + margin-left: -11px; + border-top-width: 0; + border-bottom-color: #999; + border-bottom-color: rgba(0, 0, 0, .25); +} +.popover.bottom > .arrow:after { + top: 1px; + margin-left: -10px; + content: " "; + border-top-width: 0; + border-bottom-color: #fff; +} +.popover.left > .arrow { + top: 50%; + right: -11px; + margin-top: -11px; + border-right-width: 0; + border-left-color: #999; + border-left-color: rgba(0, 0, 0, .25); +} +.popover.left > .arrow:after { + right: 1px; + bottom: -10px; + content: " "; + border-right-width: 0; + border-left-color: #fff; +} +.carousel { + position: relative; +} +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} +.carousel-inner > .item { + position: relative; + display: none; + -webkit-transition: .6s ease-in-out left; + -o-transition: .6s ease-in-out left; + transition: .6s ease-in-out left; +} +.carousel-inner > .item > img, +.carousel-inner > .item > a > img { + line-height: 1; +} +@media all and (transform-3d), (-webkit-transform-3d) { + .carousel-inner > .item { + -webkit-transition: -webkit-transform .6s ease-in-out; + -o-transition: -o-transform .6s ease-in-out; + transition: transform .6s ease-in-out; + + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-perspective: 1000; + perspective: 1000; + } + .carousel-inner > .item.next, + .carousel-inner > .item.active.right { + left: 0; + -webkit-transform: translate3d(100%, 0, 0); + transform: translate3d(100%, 0, 0); + } + .carousel-inner > .item.prev, + .carousel-inner > .item.active.left { + left: 0; + -webkit-transform: translate3d(-100%, 0, 0); + transform: translate3d(-100%, 0, 0); + } + .carousel-inner > .item.next.left, + .carousel-inner > .item.prev.right, + .carousel-inner > .item.active { + left: 0; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} +.carousel-inner > .active, +.carousel-inner > .next, +.carousel-inner > .prev { + display: block; +} +.carousel-inner > .active { + left: 0; +} +.carousel-inner > .next, +.carousel-inner > .prev { + position: absolute; + top: 0; + width: 100%; +} +.carousel-inner > .next { + left: 100%; +} +.carousel-inner > .prev { + left: -100%; +} +.carousel-inner > .next.left, +.carousel-inner > .prev.right { + left: 0; +} +.carousel-inner > .active.left { + left: -100%; +} +.carousel-inner > .active.right { + left: 100%; +} +.carousel-control { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 15%; + font-size: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); + filter: alpha(opacity=50); + opacity: .5; +} +.carousel-control.left { + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001))); + background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); + background-repeat: repeat-x; +} +.carousel-control.right { + right: 0; + left: auto; + background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5))); + background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); + background-repeat: repeat-x; +} +.carousel-control:hover, +.carousel-control:focus { + color: #fff; + text-decoration: none; + filter: alpha(opacity=90); + outline: 0; + opacity: .9; +} +.carousel-control .icon-prev, +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-left, +.carousel-control .glyphicon-chevron-right { + position: absolute; + top: 50%; + z-index: 5; + display: inline-block; +} +.carousel-control .icon-prev, +.carousel-control .glyphicon-chevron-left { + left: 50%; + margin-left: -10px; +} +.carousel-control .icon-next, +.carousel-control .glyphicon-chevron-right { + right: 50%; + margin-right: -10px; +} +.carousel-control .icon-prev, +.carousel-control .icon-next { + width: 20px; + height: 20px; + margin-top: -10px; + font-family: serif; +} +.carousel-control .icon-prev:before { + content: '\2039'; +} +.carousel-control .icon-next:before { + content: '\203a'; +} +.carousel-indicators { + position: absolute; + bottom: 10px; + left: 50%; + z-index: 15; + width: 60%; + padding-left: 0; + margin-left: -30%; + text-align: center; + list-style: none; +} +.carousel-indicators li { + display: inline-block; + width: 10px; + height: 10px; + margin: 1px; + text-indent: -999px; + cursor: pointer; + background-color: #000 \9; + background-color: rgba(0, 0, 0, 0); + border: 1px solid #fff; + border-radius: 10px; +} +.carousel-indicators .active { + width: 12px; + height: 12px; + margin: 0; + background-color: #fff; +} +.carousel-caption { + position: absolute; + right: 15%; + bottom: 20px; + left: 15%; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #fff; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, .6); +} +.carousel-caption .btn { + text-shadow: none; +} +@media screen and (min-width: 768px) { + .carousel-control .glyphicon-chevron-left, + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-prev, + .carousel-control .icon-next { + width: 30px; + height: 30px; + margin-top: -15px; + font-size: 30px; + } + .carousel-control .glyphicon-chevron-left, + .carousel-control .icon-prev { + margin-left: -15px; + } + .carousel-control .glyphicon-chevron-right, + .carousel-control .icon-next { + margin-right: -15px; + } + .carousel-caption { + right: 20%; + left: 20%; + padding-bottom: 30px; + } + .carousel-indicators { + bottom: 20px; + } +} +.clearfix:before, +.clearfix:after, +.dl-horizontal dd:before, +.dl-horizontal dd:after, +.container:before, +.container:after, +.container-fluid:before, +.container-fluid:after, +.row:before, +.row:after, +.form-horizontal .form-group:before, +.form-horizontal .form-group:after, +.btn-toolbar:before, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:before, +.btn-group-vertical > .btn-group:after, +.nav:before, +.nav:after, +.navbar:before, +.navbar:after, +.navbar-header:before, +.navbar-header:after, +.navbar-collapse:before, +.navbar-collapse:after, +.pager:before, +.pager:after, +.panel-body:before, +.panel-body:after, +.modal-footer:before, +.modal-footer:after { + display: table; + content: " "; +} +.clearfix:after, +.dl-horizontal dd:after, +.container:after, +.container-fluid:after, +.row:after, +.form-horizontal .form-group:after, +.btn-toolbar:after, +.btn-group-vertical > .btn-group:after, +.nav:after, +.navbar:after, +.navbar-header:after, +.navbar-collapse:after, +.pager:after, +.panel-body:after, +.modal-footer:after { + clear: both; +} +.center-block { + display: block; + margin-right: auto; + margin-left: auto; +} +.pull-right { + float: right !important; +} +.pull-left { + float: left !important; +} +.hide { + display: none !important; +} +.show { + display: block !important; +} +.invisible { + visibility: hidden; +} +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} +.hidden { + display: none !important; + visibility: hidden !important; +} +.affix { + position: fixed; +} +@-ms-viewport { + width: device-width; +} +.visible-xs, +.visible-sm, +.visible-md, +.visible-lg { + display: none !important; +} +.visible-xs-block, +.visible-xs-inline, +.visible-xs-inline-block, +.visible-sm-block, +.visible-sm-inline, +.visible-sm-inline-block, +.visible-md-block, +.visible-md-inline, +.visible-md-inline-block, +.visible-lg-block, +.visible-lg-inline, +.visible-lg-inline-block { + display: none !important; +} +@media (max-width: 767px) { + .visible-xs { + display: block !important; + } + table.visible-xs { + display: table; + } + tr.visible-xs { + display: table-row !important; + } + th.visible-xs, + td.visible-xs { + display: table-cell !important; + } +} +@media (max-width: 767px) { + .visible-xs-block { + display: block !important; + } +} +@media (max-width: 767px) { + .visible-xs-inline { + display: inline !important; + } +} +@media (max-width: 767px) { + .visible-xs-inline-block { + display: inline-block !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm { + display: block !important; + } + table.visible-sm { + display: table; + } + tr.visible-sm { + display: table-row !important; + } + th.visible-sm, + td.visible-sm { + display: table-cell !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-block { + display: block !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline { + display: inline !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .visible-sm-inline-block { + display: inline-block !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md { + display: block !important; + } + table.visible-md { + display: table; + } + tr.visible-md { + display: table-row !important; + } + th.visible-md, + td.visible-md { + display: table-cell !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-block { + display: block !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline { + display: inline !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .visible-md-inline-block { + display: inline-block !important; + } +} +@media (min-width: 1200px) { + .visible-lg { + display: block !important; + } + table.visible-lg { + display: table; + } + tr.visible-lg { + display: table-row !important; + } + th.visible-lg, + td.visible-lg { + display: table-cell !important; + } +} +@media (min-width: 1200px) { + .visible-lg-block { + display: block !important; + } +} +@media (min-width: 1200px) { + .visible-lg-inline { + display: inline !important; + } +} +@media (min-width: 1200px) { + .visible-lg-inline-block { + display: inline-block !important; + } +} +@media (max-width: 767px) { + .hidden-xs { + display: none !important; + } +} +@media (min-width: 768px) and (max-width: 991px) { + .hidden-sm { + display: none !important; + } +} +@media (min-width: 992px) and (max-width: 1199px) { + .hidden-md { + display: none !important; + } +} +@media (min-width: 1200px) { + .hidden-lg { + display: none !important; + } +} +.visible-print { + display: none !important; +} +@media print { + .visible-print { + display: block !important; + } + table.visible-print { + display: table; + } + tr.visible-print { + display: table-row !important; + } + th.visible-print, + td.visible-print { + display: table-cell !important; + } +} +.visible-print-block { + display: none !important; +} +@media print { + .visible-print-block { + display: block !important; + } +} +.visible-print-inline { + display: none !important; +} +@media print { + .visible-print-inline { + display: inline !important; + } +} +.visible-print-inline-block { + display: none !important; +} +@media print { + .visible-print-inline-block { + display: inline-block !important; + } +} +@media print { + .hidden-print { + display: none !important; + } +} +/*# sourceMappingURL=bootstrap.css.map */ diff --git a/docs/static/css/bootstrap.min.css b/docs/static/css/bootstrap.min.css new file mode 100644 index 0000000..db4b02f --- /dev/null +++ b/docs/static/css/bootstrap.min.css @@ -0,0 +1,27 @@ +/*! + * Bootstrap v3.3.1 (http://getbootstrap.com) + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:before,:after{color:#000!important;text-shadow:none!important;background:transparent!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}select{background:#fff!important}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered th,.table-bordered td{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:before,:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:hover,a:focus{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#777}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}mark,.mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none} + pre{ + +display: block; +padding: 9.5px; +margin: 0 0 10px; +font-size: 13px; +line-height: 1.42857143; +color: none; +word-break: break-all; +word-wrap: break-word; +background-color: none; +border: 0px solid #ccc; +border-radius: 4px; + + + } + pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll} + .container{padding-right: 15px; +padding-left: 15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-child(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=radio],input[type=checkbox]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=radio]:focus,input[type=checkbox]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#eee;opacity:1}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date],input[type=time],input[type=datetime-local],input[type=month]{line-height:34px}input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio label,.checkbox label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type=radio],.radio-inline input[type=radio],.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox]{position:absolute;margin-top:4px \9;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type=radio][disabled],input[type=checkbox][disabled],input[type=radio].disabled,input[type=checkbox].disabled,fieldset[disabled] input[type=radio],fieldset[disabled] input[type=checkbox]{cursor:not-allowed}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.form-control-static{padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm,.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm,select.form-group-sm .form-control{height:30px;line-height:30px}textarea.input-sm,textarea.form-group-sm .form-control,select[multiple].input-sm,select[multiple].form-group-sm .form-control{height:auto}.input-lg,.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-lg,select.form-group-lg .form-control{height:46px;line-height:46px}textarea.input-lg,textarea.form-group-lg .form-control,select[multiple].input-lg,select[multiple].form-group-lg .form-control{height:auto}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type=radio],.form-inline .checkbox input[type=checkbox]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.3px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#333;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{pointer-events:none;cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:hover,.btn-default:focus,.btn-default.focus,.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary:hover,.btn-primary:focus,.btn-primary.focus,.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus,.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:hover,.btn-success:focus,.btn-success.focus,.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success[disabled],fieldset[disabled] .btn-success,.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus,.btn-success.disabled:active,.btn-success[disabled]:active,fieldset[disabled] .btn-success:active,.btn-success.disabled.active,.btn-success[disabled].active,fieldset[disabled] .btn-success.active{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:hover,.btn-info:focus,.btn-info.focus,.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:hover,.btn-warning:focus,.btn-warning.focus,.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-warning,.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus,.btn-warning.disabled:active,.btn-warning[disabled]:active,fieldset[disabled] .btn-warning:active,.btn-warning.disabled.active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning.active{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:hover,.btn-danger:focus,.btn-danger.focus,.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#777;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=submit].btn-block,input[type=reset].btn-block,input[type=button].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none;visibility:hidden}.collapse.in{display:block;visibility:visible}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px solid;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#777}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px solid}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child>.btn:last-child,.btn-group>.btn-group:first-child>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=radio],[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=radio],.input-group-addon input[type=checkbox]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none;visibility:hidden}.tab-content>.active{display:block;visibility:visible}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important;visibility:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type=radio],.navbar-form .checkbox input[type=checkbox]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#333}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#fff}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:2;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:hover,.label-default[href]:focus{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding:30px 15px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding:48px 0}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-right:auto;margin-left:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item{color:#555}a.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,a.list-group-item:focus{color:#555;text-decoration:none;background-color:#f5f5f5}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,a.list-group-item-success:focus{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,a.list-group-item-info:focus{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:hover,a.list-group-item-info.active:focus{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,a.list-group-item-warning:focus{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,a.list-group-item-danger:focus{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-right:15px;padding-left:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0} +.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0} +.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:absolute;top:0;right:0;left:0;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{min-height:16.43px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:0px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-weight:400;line-height:1.4;visibility:visible;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-weight:400;line-height:1.42857143;text-align:left;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000;perspective:1000}.carousel-inner>.item.next,.carousel-inner>.item.active.right{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:hover,.carousel-control:focus{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;margin-top:-10px;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-footer:before,.modal-footer:after{display:table;content:" "}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after, +.modal-footer:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important;visibility:hidden!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none!important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table}tr.visible-xs{display:table-row!important}th.visible-xs,td.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table}tr.visible-sm{display:table-row!important}th.visible-sm,td.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table}tr.visible-md{display:table-row!important}th.visible-md,td.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table}tr.visible-lg{display:table-row!important}th.visible-lg,td.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table}tr.visible-print{display:table-row!important}th.visible-print,td.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} diff --git a/docs/static/css/liquid-dark.css b/docs/static/css/liquid-dark.css new file mode 100644 index 0000000..9cbcce5 --- /dev/null +++ b/docs/static/css/liquid-dark.css @@ -0,0 +1,105 @@ +.hs-linenum { + color: #666666; //CCCCCC; + font-style: italic; +} + +.hs-error { + background-color: #B80000 ; +} + +.hs-keyglyph, .hs-layout { /* color: red; */ + color: white; +} + +.hs-keyword { + color: #75D075 ; + //font-weight: bold; +} + +.hs-comment, .hs-comment a {color: green;} + +.hs-str, .hs-chr {color: #7FFFD4;} + +.hs-conid { + color: #00FFFF; + //font-weight: bold; +} + +.hs-definition { + color: #FFFFFF; /* #ADFF2F; */ + // font-weight: bold; +} + +.hs-varid, .hs-varop { + color: white; /* #BDDEFF; */ +} + +.hs-num, .hs-conop { + color: aquamarine; +} + +.hs-cpp { + color: orange; +} + +.hs-sel {} + +a.annot { + position:relative; + color:#000; + text-decoration:none; + white-space: pre; +} + +.hidden { + display: none; +} + +// a.annot:hover { +// z-index:25; +// background-color:#585858; +// /* background-color:#ff0 */ +// } + +a.annot span.annottext{display: none} + +a.annot:hover span.annottext{ + + border-radius: 5px 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.1); + -webkit-box-shadow: 5px 5px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 5px 5px rgba(0, 0, 0, 0.1); + + white-space:pre ; + display:block; + position: absolute; + left: 1em; top: 2em; + z-index: 9999; + margin-left: 5; + padding: 0.8em 1em; + border: 3px solid #6495ED ; // #5F9EA0; #FFAD33; + background: #EBF5FF; // #C3F6FA #F7F8FD #C1CDCD #FFFFAA; +} + +code { + /* font-weight: bold; */ + background-color: rgb(250, 250, 250); + border: 1px solid rgb(200, 200, 200); + padding-left: 4px; + padding-right: 4px; +} + +pre { + border-radius: 5px 5px; + // font-family: Bitstream Vera Sans Mono,monospace; + font-size: 100%; + // color: rgb(255, 255, 255); + // background-color: rgb(0, 0, 0); + // margin-bottom: 2em; + padding: 8px; + display: block; + overflow: visible; +} + diff --git a/docs/static/css/liquid-light.css b/docs/static/css/liquid-light.css new file mode 100644 index 0000000..6776a2f --- /dev/null +++ b/docs/static/css/liquid-light.css @@ -0,0 +1,109 @@ +.hs-keyglyph { + color: #007020 +} + +.hs-linenum { + color: white; +} + +.hs-error { + background-color: #f4abbb; +} + +.hs-keyword { + color: #007020; + // font-weight: bold; +} + +.hs-comment, .hs-comment a {color: green;} + +.hs-str, .hs-chr {color: teal;} + +.hs-conid { + color: #902000; /* color: #00FFFF; color: #0E84B5; */ + //font-weight: bold; +} + +.hs-definition { + color: #06287E + /* font-weight: bold; */ +} + +.hs-varid, .hs-varop, .hs-layout { + color: black; +} + +.hs-num { + color: #40A070; +} + +.hs-conop { + color: #902000; +} + +.hs-cpp { + color: orange; +} + +.hs-sel {} + +a.annot { + position:relative; + color:#000; + text-decoration:none; + white-space: pre; +} + +a.annot:hover { + z-index:25; + background-color: #D8D8D8; +} + +a.annot span.annottext{display: none} + +a.annot:hover span.annottext{ + + border-radius: 5px 5px; + + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.1); + -webkit-box-shadow: 5px 5px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 5px 5px rgba(0, 0, 0, 0.1); + + white-space:pre; + display:block; + position: absolute; + left: 1em; top: 2em; + z-index: 99; + margin-left: 5; + background: #FFFFAA; + border: 3px solid #FFAD33; + padding: 0.8em 1em; +} + +code { + /* font-weight: bold; + background-color: rgb(250, 250, 250); + border: 1px solid rgb(200, 200, 200); + */ + background-color: white; + color: black; + font-size: 120%; + padding-left: 4px; + padding-right: 4px; +} + +pre { + /* + background-color: #f0f0f0; + border-top: 1px solid #ccc; + border-bottom: 1px solid #ccc; + */ + padding: 5px; + font-size: 120%; + // font-family: Bitstream Vera Sans Mono,monospace; + display: block; + overflow: visible; +} diff --git a/docs/static/css/prettify.css b/docs/static/css/prettify.css new file mode 100644 index 0000000..400fd74 --- /dev/null +++ b/docs/static/css/prettify.css @@ -0,0 +1,52 @@ +/* Pretty printing styles. Used with prettify.js. */ + +/* SPAN elements with the classes below are added by prettyprint. */ +.pln { color: #000 } /* plain text */ + +@media screen { + .str { color: #080 } /* string content */ + .kwd { color: #008 } /* a keyword */ + .com { color: #800 } /* a comment */ + .typ { color: #606 } /* a type name */ + .lit { color: #066 } /* a literal value */ + /* punctuation, lisp open bracket, lisp close bracket */ + .pun, .opn, .clo { color: #660 } + .tag { color: #008 } /* a markup tag name */ + .atn { color: #606 } /* a markup attribute name */ + .atv { color: #080 } /* a markup attribute value */ + .dec, .var { color: #606 } /* a declaration; a variable name */ + .fun { color: red } /* a function name */ +} + +/* Use higher contrast and text-weight for printable form. */ +@media print, projection { + .str { color: #060 } + .kwd { color: #006; font-weight: bold } + .com { color: #600; font-style: italic } + .typ { color: #404; font-weight: bold } + .lit { color: #044 } + .pun, .opn, .clo { color: #440 } + .tag { color: #006; font-weight: bold } + .atn { color: #404 } + .atv { color: #060 } +} + +/* Put a border around prettyprinted code snippets. */ +pre.prettyprint { padding: 2px; border: 1px solid #888 } + +/* Specify class=linenums on a pre to get line numbering */ +ol.linenums { margin-top: 0; margin-bottom: 0 } /* IE indents via margin-left */ +li.L0, +li.L1, +li.L2, +li.L3, +li.L5, +li.L6, +li.L7, +li.L8 { list-style-type: none } +/* Alternate shading for lines */ +li.L1, +li.L3, +li.L5, +li.L7, +li.L9 { background: #eee } diff --git a/docs/static/css/ronacher.css b/docs/static/css/ronacher.css new file mode 100644 index 0000000..f0f3f33 --- /dev/null +++ b/docs/static/css/ronacher.css @@ -0,0 +1,119 @@ +@charset "utf-8"; + +/* fonts */ +@import url(https://fonts.googleapis.com/css?family=Ubuntu+Mono:400,400italic,700,700italic|Merriweather:400,700,400italic,700italic,300,300italic|Crimson+Text:400,400italic,600italic,600&subset=latin,latin-ext); + +/* general style */ +body { font: 17px/25px 'Merriweather', serif; + margin: 0; padding: 0; font-weight: 400; color: black; line-height: 30px; } +a { color: #398ad5; font-weight: 400; } +a:hover { color: #2771b5; } + +/* headlines */ +h1, h2, h3, h4, h5, h6 { font-family: 'Crimson Text', serif; + font-weight: 400; + color: #2771b5; + font-style: italic } +h1 { color: black; } +h1 a, h2 a, h3 a, h4 a, +h5 a, h6 a { text-decoration: none; } +h1 a:hover, h2 a:hover, +h3 a:hover, h4 a:hover { text-decoration: underline; } +h1 { margin: 15px 0 25px 0; } +h2 { margin: 25px 0 10px 0; } +h3 { margin: 35px 0 10px 0; } +h1 { font-size: 52px; line-height: 56px; } +h2 { font-size: 42px; line-height: 44px; } +h3 { font-size: 36px; line-height: 38px; } + +h1 em { color: black; font-size: 32px; display: block; + margin-left: 25px; margin-top: -15px; } + +/* layout elements */ +/* div.container { max-width: 880px; margin: 48px auto; padding: 0 40px; } + */ + +div.header { float: left; } +div.navigation { float: right; } +div.header, div.navigation { height: 25px; margin-bottom: 42px; } +div.navigation ul { margin: 0; padding: 0; list-style: none; } +div.navigation ul li { display: inline; margin: 0 2px; padding: 0; } +div.body { clear: both; margin: 0 30px; line-height: 1.6; } +body { padding-top: 50px ;} +div.footer { margin-top: 55px; font-size: 16px; + text-align: right; color: #7b8894; } +div.footer p { margin: 0; } +div.footer a { color: #7b8894; } + +/* margins and stuff */ +p, div.line-block, ul, ol, pre, + table { margin: 15px 0 15px 0; } +dt { margin: 25px 0 16px 0; padding: 0; } +dd { margin: 16px 0 25px 40px; padding: 0; } +ul ol, ol ul, ul ul, ol ol { margin: 10px 0; padding: 0 0 0 40px; } +li { padding: 0; } +h1 + p.date { margin-top: -25px; font-style: italic; } +blockquote { font-style: italic; } + +/* code formatting. no monospace because of webkit (bug?) */ +pre, code, tt { font-family: 'Ubuntu Mono', 'Consolas', 'Deja Vu Sans Mono', + 'Bitstream Vera Sans Mono', 'Monaco', 'Courier New'; + font-size: 0.9em; } +pre { line-height: 1.45; background: none; padding: 0; } +/* code, tt { background: #eee; } */ + + +/* tables */ +table { border: 1px solid #ddd; border-collapse: collapse; + background: #fafafa; } +td, th { padding: 2px 12px; border: 1px solid #ddd; } + +/* footnotes */ +table.footnote { margin: 25px 0; background: transparent; border: none; } +table.footnote + table.footnote { margin-top: -35px; } +table.footnote td { border: none; padding: 9px 0 0 0; font-size: 15px; } +table.footnote td.label { padding-right: 10px; } +table.footnote td p { margin: 0; } +table.footnote td p + p { margin-top: 15px; } + +/* blog overview */ +div.entry-overview { margin: 25px 122px 25px 102px; } +div.entry-overview h1, +div.entry-overview div.summary, +div.entry-overview div.summary p { display: inline; line-height: 30px; } +div.entry-overview h1 { margin: 0; font-size: 17px; font-weight: 700; + font-family: 'Merriweather', serif; color: black; } +div.entry-overview h1:after { content: " —"; color: black; } +div.entry-overview h1 a { color: #2771b5; } +div.entry-overview div.summary, +div.entry-overview div.date, +div.entry-overview div.summary p { margin: 0; padding: 0; } +div.entry-overview div.detail { margin-left: 140px; } +div.entry-overview div.date { float: left; width: 120px; color: #7b8894; + text-align: right; font-style: italic; + font-size: 14px; } + +/* other alignment things */ +img.align-center { margin: 15px auto; display: block; } + +/* pagination */ +div.pagination { margin: 36px 0 0 0; text-align: center; } +div.pagination strong { font-weight: normal; font-style: italic; } + +/* tags */ +p.tags { text-align: right; margin-top: 35px; } +ul.tagcloud { font-size: 16px; margin: 36px 0; padding: 0; + list-style: none; line-height: 1.45; text-align: justify } +ul.tagcloud li { margin: 0; padding: 0 10px; display: inline; } + +/* latex math */ +span.math img { margin-bottom: -7px; } + +/* ads */ +div.adspace { + text-align: center; +} + +div.adspace iframe { + margin: 25px auto 25px auto; +} diff --git a/docs/static/css/spaceg.stylesheets.css b/docs/static/css/spaceg.stylesheets.css new file mode 100644 index 0000000..2958f2c --- /dev/null +++ b/docs/static/css/spaceg.stylesheets.css @@ -0,0 +1,309 @@ +/*! +Top spaceg.stylesheets.css + spaceg.github.io by Lucas Gatsas. + www.lucasgatsas.ch + +http://lucasgatsas.ch +https://www.twitter.com/LucasGatsas + + */ + /* New Update Mo. 2 Feb. 2015 -02:33:45*/ +/* New Update Fr. 06 Feb. 2015 -01:57:13 */ +/* New Update Sa. 21. February. 2015 -02:58:16*/ +/* New Update Fr. 27. February. 2015 -21:53:19*/ +/* New Update Mo. 06. April. 2015 -09:16:07*/ +/* New Update Mo. 09. April. 2015 -14:31:47*/ +/* New Update Sa. 25. April. 2015 -16:47:32ƒ*/ +/* New Update Sa. 26. April. 2015 -00:26:13ƒ*/ +/* New Update Di. 05. May. 2015 -22:26:13ƒ*/ +/* New Update Fr. 12. May. 2015 -00:20:24ƒ*/ +/* New Update Mi. 17. Juni. 2015 - 01:13:24*/ + + .pagination{ + display:inline-block;padding-left:40px;margin:20px 0;border-radius:4px;text-align:center} + .pagination.jscroll-next-parent{display:inline-block;padding-left:40px;margin:20px 0;border-radius:4px;text-align:center} + #pagination{display:inline-block;padding-left:40px;margin:20px 0;border-radius:4px;text-align:center} + #pagination.jscroll-next-parent{display:inline-block;padding-left:40px;margin:20px 0;border-radius:4px} + .navbar-default .navbar-nav>li>a{color:#B6B6B6} + .navbar-default .navbar-toggle:hover,.navbar-default + .navbar-toggle:focus{background-color:#FFF} + .navbar-default .navbar-toggle{border-color:#FFF}i + #github-icon-top{font-size:20px} + nav.navbar.navbar-inverse.navbar-fixed-top-top{padding-top:55px} + .navbar-inverse{background-color:#FFF;border-color:#FFF}header + .intro-header{background:#6f5499;background-color:gray;background:no-repeat center center;background-attachment:scroll;-webkit-background-size:cover;-moz-background-size:cover;background-size:cover;-o-background-size:cover} + .container.front-end{width:100%} + article.container.hentry{max-width:770px;text-align:justify;margin-bottom:180px} + h1.post-title{text-align:center;color:#fff;font-weight:700;}h1 + .post-title a{text-align:center;color:#fff} + .post-meta{text-align:center;padding-top:280px} + .post-meta a{text-align:center;color:#fff}html{font-size:62.5%} + body{font-family: 'Open Sans'; + /* letter-spacing: .01rem; */ + /* font-size: 1.8rem; */ + /* line-height: 1.75em; */ + color: rgba(6,6,6,0.83); + font-size: 16px; + line-height: 28px;}p{margin:30px 0} + h1,h2,h3,h4,h5,h6{font-family:'Georgia',serif;font-weight:600}a{color:#A8A8A8} + a:hover,a:focus{color:#B6B6B6}blockquote{color:#B6B6B6;font-style:italic} + hr.small{max-width:100px;margin:15px auto;border-width:4px;border-color:#fff} + .navbar-custom{background:#fff;position:fixed;top:0;left:0;width:100%;z-index:2;font-family:'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif} + .navbar-custom .navbar-brand{font-weight:800} + .navbar-custom .nav li a{font-family:'Open Sans';text-transform:uppercase;font-size:12px;font-weight:400;letter-spacing:1px}@media only screen and (min-width: 768px){.navbar-custom{border-bottom:1px solid transparent} + .navbar-custom .navbar-brand{color:rgba(255,255,255,0.9);padding:20px} + .navbar-custom .navbar-brand:hover,.navbar-custom .navbar-brand:focus{color:rgba(255,255,255,0.8)} + .navbar-custom .nav li a{padding:20px}.navbar-custom .nav li a:hover,.navbar-custom .nav li a:focus{color:rgba(0,0,0,0.59)}}@media only screen and (min-width: 1170px){.navbar-custom{-webkit-transition:background-color .3s;-moz-transition:background-color .3s;transition:background-color .3s;-webkit-transform:translate3d(0,0,0);-moz-transform:translate3d(0,0,0);-ms-transform:translate3d(0,0,0);-o-transform:translate3d(0,0,0);transform:translate3d(0,0,0);-webkit-backface-visibility:hidden;backface-visibility:hidden} + .navbar-custom.is-fixed{position:fixed;top:-65px;background-color:rgba(255,255,255,0.9);border-bottom:1px solid #f2f2f2;-webkit-transition:-webkit-transform .3s;-moz-transition:-moz-transform .3s;transition:transform .3s} + .navbar-custom.is-fixed .navbar-brand{color:#404040} + .navbar-custom.is-fixed .navbar-brand:hover,.navbar-custom.is-fixed .navbar-brand:focus{color:#777} + .navbar-custom.is-fixed .nav li a{color:rgba(64,64,64,0.49)}.navbar-custom.is-fixed .nav li a:hover,.navbar-custom.is-fixed .nav li a:focus{color:#777} + .navbar-custom.is-visible{-webkit-transform:translate3d(0,100%,0);-moz-transform:translate3d(0,100%,0);-ms-transform:translate3d(0,100%,0);-o-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}} + .intro-header{background-color:gray;background:no-repeat center center;background-attachment:scroll;-webkit-background-size:cover;-moz-background-size:cover;background-size:cover;-o-background-size:cover;margin-bottom:50px;min-height:473px} + .intro-header + .site-heading, + .intro-header + .post-heading, + .intro-header + .page-heading{padding:100px 0 50px;color:#000; text-align: center;}@media only screen and (min-width: 768px){.intro-header .site-heading,.intro-header .post-heading,.intro-header .page-heading{padding:250px 0}} + .intro-header .site-heading,.intro-header .page-heading{text-align:center} + .intro-header .site-heading h1,.intro-header .page-heading h1{margin-top:0;font-size:50px} + .intro-header + .site-heading .subheading,.intro-header .page-heading .subheading{ + font-size: 18px; + line-height: 1.1,display: block; + font-family: 'Georgia' Times,serif; + margin: 10px 0 0; + color: rgba(11, 11, 11, 0.57); + font-weight: 100; + letter-spacing: 0; + font-weight: 400; + text-rendering: optimizeLegibility; + + }@media only screen and (min-width: 768px){.intro-header .site-heading h1,.intro-header .page-heading h1{font-size:60px}} + .intro-header .post-heading h1{font-size:35px;} + .intro-header .post-heading .subheading,.intro-header .post-heading .meta{line-height:1.1;display:block} + .intro-header .post-heading .subheading{font-family:'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif;font-size:24px;margin:10px 0 30px;font-weight:400;} + .intro-header .post-heading .meta{font-family:'Playfair Display', Times, serif;font-style:italic;font-weight:300;font-size:20px;} + .intro-header .post-heading .meta a{} +@media only screen and (min-width: 768px){.intro-header .post-heading h1{font-size:55px} + .intro-header .post-heading .subheading{font-size:30px;}} + .post-preview > a{color:#404040}.post-preview > a:hover,.post-preview > a:focus{text-decoration:none;color:#BBB} + .post-preview > a > .post-title{font-size:30px;margin-top:30px;margin-bottom:10px} + .post-preview > a > .post-subtitle{margin:0;font-weight:300;margin-bottom:10px} + .post-preview > .post-meta{color:gray;font-size:16px;margin-top:0} + .post-preview > .post-meta > a{text-decoration:none;color:#404040} + .post-preview > .post-meta > a:hover,.post-preview > .post-meta > a:focus{color:#BBB;text-decoration:underline}@media only screen and (min-width: 768px){.post-preview > a > .post-title{font-size:36px}} + .section-heading{font-size:36px;/*margin-top:60px;*/font-weight:500;color:rgba(0,0,0,0.83);text-align:center} + .caption{text-align:center;font-size:14px;padding:10px;font-style:italic;margin:15px 0 0;display:block;border-bottom-right-radius:5px;border-bottom-left-radius:5px; border-top: 1px solid #EEE; }footer{padding:50px 0 65px}footer + .list-inline{margin:0;padding:0}footer .copyright{font-size:14px;text-align:center;margin-bottom:0} + .floating-label-form-group{font-size:14px;position:relative;margin-bottom:0;padding-bottom:.5em;border-bottom:1px solid #eee} + .floating-label-form-group input,.floating-label-form-group textarea{z-index:1;position:relative;padding-right:0;padding-left:0;border:none;border-radius:0;font-size:1.5em;background:none;box-shadow:none!important;resize:none} + .floating-label-form-group label{display:block;z-index:0;position:relative;top:2em;margin:0;font-size:.85em;line-height:1.764705882em;vertical-align:middle;vertical-align:baseline;opacity:0;-webkit-transition:top .3s ease,opacity .3s ease;-moz-transition:top .3s ease,opacity .3s ease;-ms-transition:top .3s ease,opacity .3s ease;transition:top .3s ease,opacity .3s ease} + .floating-label-form-group::not(:first-child){padding-left:14px;border-left:1px solid #eee} + .floating-label-form-group-with-value label{top:0;opacity:1} + .floating-label-form-group-with-focus label{color:#0085a1} + form .row:first-child .floating-label-form-group{border-top:1px solid #eee} + .btn{font-family:'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif;text-transform:uppercase;font-size:14px;font-weight:800;letter-spacing:1px;border-radius:90px;padding:15px 25px} + .btn-lg{font-size:16px;padding:25px 35px}.btn-default:hover,.btn-default:focus{background-color:#000;border:1px solid #000;color:#fff} + .pager{margin:20px 0 0} + .pager li > a,.pager li > span{font-family:'Open Sans','Helvetica Neue',Helvetica,Arial,sans-serif;text-transform:uppercase;font-size:14px;font-weight:800;letter-spacing:1px;padding:10px 5px;background-color:#fff;border-radius:0}@media only screen and (min-width: 768px){.pager li > a,.pager li > span{font-size:14px;padding:15px 25px}} + .pager li > a:hover,.pager li > a:focus{color:#fff;background-color:#0D0D0D;border:1px solid #0D0D0D} + .pager .disabled > a,.pager .disabled > a:hover,.pager .disabled > a:focus,.pager .disabled > span{color:gray;background-color:#404040;cursor:not-allowed}::-moz-selection{color:#fff;text-shadow:none;background:#0085a1} + ::selection{color:#fff;text-shadow:none;background:#404040}img::selection{color:#fff;background:transparent}img::-moz-selection{color:#fff;background:transparent} + body{webkit-tap-highlight-color:#0085a1}code{padding:2px 4px;font-size:90%;color:#969696;background-color:#f9f2f4;border-radius:4px} + nav.navbar.navbar-inverse.navbar-fixed-bottom{background-color:#FFF;border-color:#E2E2E2} + .navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#222;background-color:transparent} + img#small-img-nav{margin-bottom:40px;margin-top:-10px}.navbar-inverse .navbar-toggle .icon-bar{background-color:#717171} + .navbar-nav{float:left;margin:0;font-family:'Playfair Display',serif;font-size:14px;font-weight:300px} + ul.nav.navbar-nav{font-weight:400}.pager li > a,.pager li > span{font-size:14px;padding:15px 25px;border-radius:90px} + a#roundbutton{border:1px solid rgba(200,200,200,0.83);border-radius:90px;padding:7px;margin-top:13px;padding-right:13px;padding-left:13px;color:#d3d3d3;font-weight:700;font-family:'Open Sans'} + a#roundbutton:hover{border:1px solid #000;border-radius:90px;padding:7px;margin-top:13px;padding-right:13px;padding-left:13px;color:#fff;background:#000} + a#post-title-h1 {color: white;} + h1#top-title-font-weight-bold{ + + + font-family: "Georgia"; */ + /* font-weight: 800; */ + /* text-shadow: rgba(0,0,0,0.4) 0 1px 1px; */ + /* letter-spacing: -3px; */ + /* color: rgba(15, 14, 14, 0.8); */ + font-family: Georgia,times,arial; + font-size: 36px; + font-weight: 300; + margin-top: -5px; + color: rgba(15, 14, 14, 0.95); + + } + +/* The Colors for the Social Icons */ + + +i.fa.fa-rss.fa-stack-1x.fa-inverse { +background: white; +color: gray; +} +i.fa.fa-twitter.fa-stack-1x.fa-inverse { +background: white; +color: gray; +} +i.fa.fa-google-plus.fa-stack-1x.fa-inverse { +background: white; +color: gray; +} +i.fa.fa-github.fa-stack-1x.fa-inverse { +background: white; +color: gray; +} +i.fa.fa-instagram.fa-stack-1x.fa-inverse { + background: white; + color: gray; +} + +/* The Hovers for the Social Icons */ + + +i.fa.fa-rss.fa-stack-1x.fa-inverse:hover { +background: white; +color: rgb(194, 194, 194); +} +i.fa.fa-twitter.fa-stack-1x.fa-inverse:hover { +background: white; +color: rgb(194, 194, 194); +} +i.fa.fa-google-plus.fa-stack-1x.fa-inverse:hover { +background: white; +color: rgb(194, 194, 194); +} +i.fa.fa-github.fa-stack-1x.fa-inverse:hover { +background: white; +color: rgb(194, 194, 194); +} +i.fa.fa-instagram.fa-stack-1x.fa-inverse:hover { + background: white; + color: gray; +} + + + +/* Modal Css for Social Buttons */ + +i#spaceg-social-modal { +font-size: 19px; +padding-top: 30px; +color: rgb(197, 197, 197); +} + +i#spaceg-social-modal:hover { +font-size: 19px; +padding-top: 30px; +color: rgb(174, 174, 174); +} +a#blog-title-left-top{font-family:Georgia,times,arial;font-size:36px;font-weight:300;margin-top:-5px;color: rgba(15, 14, 14, 0.8);}@media (max-width: 768px){button#toggle-bottom-fadeout{display:none}.navbar-inverse .navbar-nav>li>a{color:#000;color:#282828}section#archive{padding-top:100px}ul,ol{margin-top:0;margin-bottom:10px;list-style:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:none;word-break:break-all;word-wrap:break-word;background-color:none;border:0 solid #ccc;border-radius:4px}.infinite-spinner{display:block;width:40px;height:40px;border-radius:40px;background-color:#333;margin:60px auto;-webkit-animation:scaleout 1s infinite ease-in-out;animation:scaleout 1s infinite ease-in-out}}@-webkit-keyframes scaleout{0%{-webkit-transform:scale(0.0)}100%{-webkit-transform:scale(1.0);opacity:0}}@keyframes scaleout{0%{transform:scale(0.0);-webkit-transform:scale(0.0)}100%{transform:scale(1.0);-webkit-transform:scale(1.0);opacity:0}} + + + + +/* Table Settings */ +table { + background-color: rgba(152, 152, 152, 0); + text-align: initial; +} + +@media (max-width: 533px) { + +th#fadeout-1 { + display: none; +} +td#fadeout-1 { + display: none; +}} +/* End New Table Settings fade out at 533px hurra :) ! */ + + + + +@media (max-width: 768px) { +.navbar-default .navbar-collapse,.navbar-default .navbar-form { + border-color: #e7e7e7; + margin-bottom: 3px; +} +} + +.img-responsive, .thumbnail > img, .thumbnail a > img, .carousel-inner > .item > img, .carousel-inner > .item > a > img { + display: inline-block; + max-width: 100%; + height: auto; +} +.footer-block, .hero-block { + height: 390px; + width: 97%; + position: absolute; + top: 60%; + margin-top: -305px; +} +@media (max-width: 768px) { +.footer-block, .hero-block { + height: 390px; + width: 96%; + position: absolute; + top: 50%; + margin-top: -305px; + display:none; +} +} +@media (min-width: 768px) { +img#small-img-nav { + display:none; + margin-bottom: 40px; + margin-top: -10px; +}} +.img-circle { + border-radius: 50%; +} +p.font-style-inline-small{font-size:13px;color:rgba(128,128,128,0.74)}h1.font-style-inline-small-h1{font-family:'Playfair Display',serif;font-weight:100;color:#3a3a3a}.portfolio-modal .modal-content{padding:100px 0;min-height:100%;border:0;border-radius:0;text-align:center;background-clip:border-box;-webkit-box-shadow:none;box-shadow:none}@media (min-width: 768px){.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,0.5);box-shadow:0 5px 15px rgba(0,0,0,0.5)}}.modal-content{position:relative;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 3px 9px rgba(0,0,0,0.5);box-shadow:0 3px 9px rgba(0,0,0,0.5);-webkit-background-clip:padding-box;background-clip:padding-box;outline:0}.portfolio-modal .close-modal{position:absolute;top:25px;right:25px;width:75px;height:75px;background-color:transparent;cursor:pointer}.modal-open .modal{overflow-x:hidden;overflow-y:hidden}.fade.in{opacity:1}.modal{display:none;overflow:hidden;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;-webkit-overflow-scrolling:touch;outline:0;margin-right:-20px}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.portfolio-modal .modal-content{padding:100px 0;min-height:100%;border:0;border-radius:0;text-align:center;background-clip:border-box;-webkit-box-shadow:none;box-shadow:none}.portfolio-modal .modal-content h2{margin:0;font-size:3em}.portfolio-modal .modal-content img{margin-bottom:30px;border-radius:90%;height:210px}.portfolio-modal .modal-content .item-details{margin:30px 0}.portfolio-modal .close-modal{position:absolute;top:25px;right:25px;width:75px;height:75px;background-color:transparent;cursor:pointer}.portfolio-modal .close-modal:hover{opacity:.3}.portfolio-modal .close-modal .lr{z-index:1051;width:1px;height:75px;margin-left:35px;background-color:#2c3e50;-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg)}.portfolio-modal .close-modal .lr .rl{z-index:1052;width:1px;height:75px;background-color:#2c3e50;-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.portfolio-modal .modal-backdrop{display:none;opacity:0}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:-webkit-inline-box;max-width:100%;height:auto}.spaceg-content{text-align:center}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#555;background-color:rgba(231,231,231,0)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#262626;text-decoration:none;background-color:rgba(255,255,255,0)} +.container.head{ + width:100%; + max-height: 400px; + /*background:-webkit-gradient(linear,50% 0%,50% 100%,color-stop(0%, + rgba(129,129,129,0.11)),color-stop(100%, + rgba(6,6,6,0.68))); + background:-webkit-linear-gradient(top, + rgba(76,76,76,0.34) 0%, + rgba(215,215,215,0.63) 100%); + background:-moz-linear-gradient(top, + rgba(76,76,76,0.34) 0%, + rgba(215,215,215,0.63) 100%); + background:-o-linear-gradient(top, + rgba(76,76,76,0.34) 0%, + rgba(215,215,215,0.63) 100%); + background:linear-gradient(top, + rgba(76,76,76,0.34) 0%, + rgba(215,215,215,0.63) 100%)*/} + .intro-header { +color: #000; + + } + + h1.post-title-readmore { +text-align: center; +font-size: 13px; +font-weight: 400; +color: white; +} +a#readmore-link { +color: white; +} +i#icon-top { +font-size: 13px; +} + + +img.img-circle.portfolio-link.img-responsive { + cursor: pointer; +} + + + + + diff --git a/docs/static/css/syntax.css b/docs/static/css/syntax.css new file mode 100644 index 0000000..57b31eb --- /dev/null +++ b/docs/static/css/syntax.css @@ -0,0 +1,18 @@ +/* Generated by pandoc. */ +table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode, table.sourceCode pre + { margin: 0; padding: 0; border: 0; vertical-align: baseline; border: none; } +td.lineNumbers { border-right: 1px solid #AAAAAA; text-align: right; color: #AAAAAA; padding-right: 5px; padding-left: 5px; } +td.sourceCode { padding-left: 5px; } +.sourceCode span.kw { color: #007020; font-weight: bold; } +.sourceCode span.dt { color: #902000; } +.sourceCode span.dv { color: #40a070; } +.sourceCode span.bn { color: #40a070; } +.sourceCode span.fl { color: #40a070; } +.sourceCode span.ch { color: #4070a0; } +.sourceCode span.st { color: #4070a0; } +.sourceCode span.co { color: #60a0b0; font-style: italic; } +.sourceCode span.ot { color: #007020; } +.sourceCode span.al { color: red; font-weight: bold; } +.sourceCode span.fu { color: #06287e; } +.sourceCode span.re { } +.sourceCode span.er { color: red; font-weight: bold; } diff --git a/docs/static/img/Eiffel.jpg b/docs/static/img/Eiffel.jpg new file mode 100644 index 0000000..a249b0e Binary files /dev/null and b/docs/static/img/Eiffel.jpg differ diff --git a/docs/static/img/Eiffel.png b/docs/static/img/Eiffel.png new file mode 100644 index 0000000..021b96f Binary files /dev/null and b/docs/static/img/Eiffel.png differ diff --git a/docs/static/img/HomerAngel.jpg b/docs/static/img/HomerAngel.jpg new file mode 100644 index 0000000..efd121c Binary files /dev/null and b/docs/static/img/HomerAngel.jpg differ diff --git a/docs/static/img/Peter_Landin.png b/docs/static/img/Peter_Landin.png new file mode 100644 index 0000000..5fe68a4 Binary files /dev/null and b/docs/static/img/Peter_Landin.png differ diff --git a/docs/static/img/anatomy.png b/docs/static/img/anatomy.png new file mode 100644 index 0000000..e7009f2 Binary files /dev/null and b/docs/static/img/anatomy.png differ diff --git a/docs/static/img/angles.jpg b/docs/static/img/angles.jpg new file mode 100644 index 0000000..58521e2 Binary files /dev/null and b/docs/static/img/angles.jpg differ diff --git a/docs/static/img/ansel-adams-la-jolla.jpg b/docs/static/img/ansel-adams-la-jolla.jpg new file mode 100644 index 0000000..fab62a2 Binary files /dev/null and b/docs/static/img/ansel-adams-la-jolla.jpg differ diff --git a/docs/static/img/arrival-symbol.jpg b/docs/static/img/arrival-symbol.jpg new file mode 100644 index 0000000..9eae365 Binary files /dev/null and b/docs/static/img/arrival-symbol.jpg differ diff --git a/docs/static/img/art_g_sample.jpg b/docs/static/img/art_g_sample.jpg new file mode 100644 index 0000000..3aa9049 Binary files /dev/null and b/docs/static/img/art_g_sample.jpg differ diff --git a/docs/static/img/baker-aker.jpg b/docs/static/img/baker-aker.jpg new file mode 100644 index 0000000..559490d Binary files /dev/null and b/docs/static/img/baker-aker.jpg differ diff --git a/docs/static/img/beach.jpg b/docs/static/img/beach.jpg new file mode 100644 index 0000000..7c8008a Binary files /dev/null and b/docs/static/img/beach.jpg differ diff --git a/docs/static/img/beta-step.png b/docs/static/img/beta-step.png new file mode 100644 index 0000000..118ee15 Binary files /dev/null and b/docs/static/img/beta-step.png differ diff --git a/docs/static/img/billionaire.jpg b/docs/static/img/billionaire.jpg new file mode 100644 index 0000000..f2847e7 Binary files /dev/null and b/docs/static/img/billionaire.jpg differ diff --git a/docs/static/img/blott-wadler.png b/docs/static/img/blott-wadler.png new file mode 100644 index 0000000..a05f3d8 Binary files /dev/null and b/docs/static/img/blott-wadler.png differ diff --git a/docs/static/img/books.jpg b/docs/static/img/books.jpg new file mode 100644 index 0000000..0f1e4de Binary files /dev/null and b/docs/static/img/books.jpg differ diff --git a/docs/static/img/breaking-chain.jpg b/docs/static/img/breaking-chain.jpg new file mode 100644 index 0000000..019bc57 Binary files /dev/null and b/docs/static/img/breaking-chain.jpg differ diff --git a/docs/static/img/cake.png b/docs/static/img/cake.png new file mode 100644 index 0000000..fe21c29 Binary files /dev/null and b/docs/static/img/cake.png differ diff --git a/docs/static/img/carmack-tweet-function.png b/docs/static/img/carmack-tweet-function.png new file mode 100644 index 0000000..79727a7 Binary files /dev/null and b/docs/static/img/carmack-tweet-function.png differ diff --git a/docs/static/img/carmack-tweet.png b/docs/static/img/carmack-tweet.png new file mode 100644 index 0000000..4cbac10 Binary files /dev/null and b/docs/static/img/carmack-tweet.png differ diff --git a/docs/static/img/color1.jpg b/docs/static/img/color1.jpg new file mode 100644 index 0000000..bd4bf2f Binary files /dev/null and b/docs/static/img/color1.jpg differ diff --git a/docs/static/img/color2.jpg b/docs/static/img/color2.jpg new file mode 100644 index 0000000..83bac71 Binary files /dev/null and b/docs/static/img/color2.jpg differ diff --git a/docs/static/img/color3.jpg b/docs/static/img/color3.jpg new file mode 100644 index 0000000..13ce9c5 Binary files /dev/null and b/docs/static/img/color3.jpg differ diff --git a/docs/static/img/data-box.png b/docs/static/img/data-box.png new file mode 100644 index 0000000..865c59e Binary files /dev/null and b/docs/static/img/data-box.png differ diff --git a/docs/static/img/data-para-type.png b/docs/static/img/data-para-type.png new file mode 100644 index 0000000..52f23c3 Binary files /dev/null and b/docs/static/img/data-para-type.png differ diff --git a/docs/static/img/data-para-val.png b/docs/static/img/data-para-val.png new file mode 100644 index 0000000..4ce7536 Binary files /dev/null and b/docs/static/img/data-para-val.png differ diff --git a/docs/static/img/dr-evil.jpg b/docs/static/img/dr-evil.jpg new file mode 100644 index 0000000..1ccfead Binary files /dev/null and b/docs/static/img/dr-evil.jpg differ diff --git a/docs/static/img/egg-eater.jpg b/docs/static/img/egg-eater.jpg new file mode 100644 index 0000000..cbb4ee9 Binary files /dev/null and b/docs/static/img/egg-eater.jpg differ diff --git a/docs/static/img/fairy.png b/docs/static/img/fairy.png new file mode 100644 index 0000000..0e2797d Binary files /dev/null and b/docs/static/img/fairy.png differ diff --git a/docs/static/img/fargo.jpg b/docs/static/img/fargo.jpg new file mode 100644 index 0000000..458a7c1 Binary files /dev/null and b/docs/static/img/fargo.jpg differ diff --git a/docs/static/img/farmer.jpg b/docs/static/img/farmer.jpg new file mode 100644 index 0000000..a804964 Binary files /dev/null and b/docs/static/img/farmer.jpg differ diff --git a/docs/static/img/fellini.png b/docs/static/img/fellini.png new file mode 100644 index 0000000..de01ef2 Binary files /dev/null and b/docs/static/img/fellini.png differ diff --git a/docs/static/img/filter-pattern-instance.png b/docs/static/img/filter-pattern-instance.png new file mode 100644 index 0000000..28d85ee Binary files /dev/null and b/docs/static/img/filter-pattern-instance.png differ diff --git a/docs/static/img/filter-pattern.png b/docs/static/img/filter-pattern.png new file mode 100644 index 0000000..45db059 Binary files /dev/null and b/docs/static/img/filter-pattern.png differ diff --git a/docs/static/img/foldl-pattern.png b/docs/static/img/foldl-pattern.png new file mode 100644 index 0000000..0e41265 Binary files /dev/null and b/docs/static/img/foldl-pattern.png differ diff --git a/docs/static/img/foldr-pattern-instance.png b/docs/static/img/foldr-pattern-instance.png new file mode 100644 index 0000000..f0fdc37 Binary files /dev/null and b/docs/static/img/foldr-pattern-instance.png differ diff --git a/docs/static/img/foldr-pattern.png b/docs/static/img/foldr-pattern.png new file mode 100644 index 0000000..6b02aaa Binary files /dev/null and b/docs/static/img/foldr-pattern.png differ diff --git a/docs/static/img/galaxy-brain-130.jpg b/docs/static/img/galaxy-brain-130.jpg new file mode 100644 index 0000000..687041d Binary files /dev/null and b/docs/static/img/galaxy-brain-130.jpg differ diff --git a/docs/static/img/geisel2.jpg b/docs/static/img/geisel2.jpg new file mode 100644 index 0000000..b3683e8 Binary files /dev/null and b/docs/static/img/geisel2.jpg differ diff --git a/docs/static/img/gotham.jpg b/docs/static/img/gotham.jpg new file mode 100644 index 0000000..ccc8e0e Binary files /dev/null and b/docs/static/img/gotham.jpg differ diff --git a/docs/static/img/gray2.jpg b/docs/static/img/gray2.jpg new file mode 100644 index 0000000..054ede5 Binary files /dev/null and b/docs/static/img/gray2.jpg differ diff --git a/docs/static/img/gray3.jpg b/docs/static/img/gray3.jpg new file mode 100644 index 0000000..0f9aa80 Binary files /dev/null and b/docs/static/img/gray3.jpg differ diff --git a/docs/static/img/gui-button-ff.png b/docs/static/img/gui-button-ff.png new file mode 100644 index 0000000..87c5e3c Binary files /dev/null and b/docs/static/img/gui-button-ff.png differ diff --git a/docs/static/img/gui-button-submit.png b/docs/static/img/gui-button-submit.png new file mode 100644 index 0000000..0793a66 Binary files /dev/null and b/docs/static/img/gui-button-submit.png differ diff --git a/docs/static/img/haskell98-classes.png b/docs/static/img/haskell98-classes.png new file mode 100644 index 0000000..914226c Binary files /dev/null and b/docs/static/img/haskell98-classes.png differ diff --git a/docs/static/img/ico.jpg b/docs/static/img/ico.jpg new file mode 100644 index 0000000..69a4bcb Binary files /dev/null and b/docs/static/img/ico.jpg differ diff --git a/docs/static/img/ico.png b/docs/static/img/ico.png new file mode 100644 index 0000000..487b180 Binary files /dev/null and b/docs/static/img/ico.png differ diff --git a/docs/static/img/info_parser.001.jpg b/docs/static/img/info_parser.001.jpg new file mode 100644 index 0000000..b6bd5aa Binary files /dev/null and b/docs/static/img/info_parser.001.jpg differ diff --git a/docs/static/img/info_parser.001a.jpg b/docs/static/img/info_parser.001a.jpg new file mode 100644 index 0000000..b0dc541 Binary files /dev/null and b/docs/static/img/info_parser.001a.jpg differ diff --git a/docs/static/img/info_parser.001b.jpg b/docs/static/img/info_parser.001b.jpg new file mode 100644 index 0000000..dfbf267 Binary files /dev/null and b/docs/static/img/info_parser.001b.jpg differ diff --git a/docs/static/img/info_parser.002.jpg b/docs/static/img/info_parser.002.jpg new file mode 100644 index 0000000..9cbbcfe Binary files /dev/null and b/docs/static/img/info_parser.002.jpg differ diff --git a/docs/static/img/info_parser.003.jpg b/docs/static/img/info_parser.003.jpg new file mode 100644 index 0000000..4e5f1e7 Binary files /dev/null and b/docs/static/img/info_parser.003.jpg differ diff --git a/docs/static/img/la-jolla-shores-2.jpg b/docs/static/img/la-jolla-shores-2.jpg new file mode 100644 index 0000000..2554ea9 Binary files /dev/null and b/docs/static/img/la-jolla-shores-2.jpg differ diff --git a/docs/static/img/la-jolla-shores.jpg b/docs/static/img/la-jolla-shores.jpg new file mode 100644 index 0000000..1f96fdd Binary files /dev/null and b/docs/static/img/la-jolla-shores.jpg differ diff --git a/docs/static/img/lambda-calculus.png b/docs/static/img/lambda-calculus.png new file mode 100644 index 0000000..c5d1bbe Binary files /dev/null and b/docs/static/img/lambda-calculus.png differ diff --git a/docs/static/img/list-tree.png b/docs/static/img/list-tree.png new file mode 100644 index 0000000..ca7aefd Binary files /dev/null and b/docs/static/img/list-tree.png differ diff --git a/docs/static/img/logic_boat.png b/docs/static/img/logic_boat.png new file mode 100644 index 0000000..7c4d347 Binary files /dev/null and b/docs/static/img/logic_boat.png differ diff --git a/docs/static/img/logo-header.png b/docs/static/img/logo-header.png new file mode 100644 index 0000000..d9cffa3 Binary files /dev/null and b/docs/static/img/logo-header.png differ diff --git a/docs/static/img/logo.png b/docs/static/img/logo.png new file mode 100755 index 0000000..6a2b486 Binary files /dev/null and b/docs/static/img/logo.png differ diff --git a/docs/static/img/map-pattern-instance.png b/docs/static/img/map-pattern-instance.png new file mode 100644 index 0000000..cbf0a31 Binary files /dev/null and b/docs/static/img/map-pattern-instance.png differ diff --git a/docs/static/img/map-pattern.png b/docs/static/img/map-pattern.png new file mode 100644 index 0000000..28567b7 Binary files /dev/null and b/docs/static/img/map-pattern.png differ diff --git a/docs/static/img/martin_odersky.jpg b/docs/static/img/martin_odersky.jpg new file mode 100644 index 0000000..bc994ae Binary files /dev/null and b/docs/static/img/martin_odersky.jpg differ diff --git a/docs/static/img/matrix.jpg b/docs/static/img/matrix.jpg new file mode 100644 index 0000000..023d8ce Binary files /dev/null and b/docs/static/img/matrix.jpg differ diff --git a/docs/static/img/moo1.png b/docs/static/img/moo1.png new file mode 100644 index 0000000..079c8f6 Binary files /dev/null and b/docs/static/img/moo1.png differ diff --git a/docs/static/img/moo2.png b/docs/static/img/moo2.png new file mode 100644 index 0000000..74fe596 Binary files /dev/null and b/docs/static/img/moo2.png differ diff --git a/docs/static/img/moo3.png b/docs/static/img/moo3.png new file mode 100644 index 0000000..088d20f Binary files /dev/null and b/docs/static/img/moo3.png differ diff --git a/docs/static/img/morpheus.png b/docs/static/img/morpheus.png new file mode 100644 index 0000000..aa4855b Binary files /dev/null and b/docs/static/img/morpheus.png differ diff --git a/docs/static/img/music-score.png b/docs/static/img/music-score.png new file mode 100644 index 0000000..1860409 Binary files /dev/null and b/docs/static/img/music-score.png differ diff --git a/docs/static/img/nqueens.png b/docs/static/img/nqueens.png new file mode 100644 index 0000000..0f2aff7 Binary files /dev/null and b/docs/static/img/nqueens.png differ diff --git a/docs/static/img/out.gif b/docs/static/img/out.gif new file mode 100644 index 0000000..871466e Binary files /dev/null and b/docs/static/img/out.gif differ diff --git a/docs/static/img/preloader.gif b/docs/static/img/preloader.gif new file mode 100644 index 0000000..facfd22 Binary files /dev/null and b/docs/static/img/preloader.gif differ diff --git a/docs/static/img/prolog_tree1.png b/docs/static/img/prolog_tree1.png new file mode 100644 index 0000000..af07464 Binary files /dev/null and b/docs/static/img/prolog_tree1.png differ diff --git a/docs/static/img/prolog_tree2.png b/docs/static/img/prolog_tree2.png new file mode 100644 index 0000000..d36507e Binary files /dev/null and b/docs/static/img/prolog_tree2.png differ diff --git a/docs/static/img/prolog_tree3.png b/docs/static/img/prolog_tree3.png new file mode 100644 index 0000000..a355e76 Binary files /dev/null and b/docs/static/img/prolog_tree3.png differ diff --git a/docs/static/img/prolog_unify_1.png b/docs/static/img/prolog_unify_1.png new file mode 100644 index 0000000..f0f9f9d Binary files /dev/null and b/docs/static/img/prolog_unify_1.png differ diff --git a/docs/static/img/prolog_unify_2.png b/docs/static/img/prolog_unify_2.png new file mode 100644 index 0000000..42b6307 Binary files /dev/null and b/docs/static/img/prolog_unify_2.png differ diff --git a/docs/static/img/prolog_unify_3.png b/docs/static/img/prolog_unify_3.png new file mode 100644 index 0000000..ed69f07 Binary files /dev/null and b/docs/static/img/prolog_unify_3.png differ diff --git a/docs/static/img/prolog_unify_4.png b/docs/static/img/prolog_unify_4.png new file mode 100644 index 0000000..d3f05fd Binary files /dev/null and b/docs/static/img/prolog_unify_4.png differ diff --git a/docs/static/img/prolog_unify_5.png b/docs/static/img/prolog_unify_5.png new file mode 100644 index 0000000..e02e6f1 Binary files /dev/null and b/docs/static/img/prolog_unify_5.png differ diff --git a/docs/static/img/prolog_unify_6.png b/docs/static/img/prolog_unify_6.png new file mode 100644 index 0000000..123b274 Binary files /dev/null and b/docs/static/img/prolog_unify_6.png differ diff --git a/docs/static/img/prolog_unify_7.png b/docs/static/img/prolog_unify_7.png new file mode 100644 index 0000000..18f2879 Binary files /dev/null and b/docs/static/img/prolog_unify_7.png differ diff --git a/docs/static/img/queue.png b/docs/static/img/queue.png new file mode 100644 index 0000000..ddc94ef Binary files /dev/null and b/docs/static/img/queue.png differ diff --git a/docs/static/img/road.jpg b/docs/static/img/road.jpg new file mode 100644 index 0000000..7c9e469 Binary files /dev/null and b/docs/static/img/road.jpg differ diff --git a/docs/static/img/scala_logo.png b/docs/static/img/scala_logo.png new file mode 100644 index 0000000..9cdb2c6 Binary files /dev/null and b/docs/static/img/scala_logo.png differ diff --git a/docs/static/img/sea.jpg b/docs/static/img/sea.jpg new file mode 100644 index 0000000..068cd1b Binary files /dev/null and b/docs/static/img/sea.jpg differ diff --git a/docs/static/img/seating-WLH-2005-groups.pdf b/docs/static/img/seating-WLH-2005-groups.pdf new file mode 100644 index 0000000..f1ab6cb Binary files /dev/null and b/docs/static/img/seating-WLH-2005-groups.pdf differ diff --git a/docs/static/img/seating-WLH-2005.pdf b/docs/static/img/seating-WLH-2005.pdf new file mode 100644 index 0000000..cbf367e Binary files /dev/null and b/docs/static/img/seating-WLH-2005.pdf differ diff --git a/docs/static/img/sky.jpg b/docs/static/img/sky.jpg new file mode 100644 index 0000000..e52dea7 Binary files /dev/null and b/docs/static/img/sky.jpg differ diff --git a/docs/static/img/spock_logic.jpg b/docs/static/img/spock_logic.jpg new file mode 100644 index 0000000..4c284f3 Binary files /dev/null and b/docs/static/img/spock_logic.jpg differ diff --git a/docs/static/img/terminator.jpg b/docs/static/img/terminator.jpg new file mode 100644 index 0000000..5cb91d0 Binary files /dev/null and b/docs/static/img/terminator.jpg differ diff --git a/docs/static/img/tree-data-leaf.png b/docs/static/img/tree-data-leaf.png new file mode 100644 index 0000000..d823a92 Binary files /dev/null and b/docs/static/img/tree-data-leaf.png differ diff --git a/docs/static/img/tree-data-node.png b/docs/static/img/tree-data-node.png new file mode 100644 index 0000000..6ed8c37 Binary files /dev/null and b/docs/static/img/tree-data-node.png differ diff --git a/docs/static/img/trinity.png b/docs/static/img/trinity.png new file mode 100644 index 0000000..34b2854 Binary files /dev/null and b/docs/static/img/trinity.png differ diff --git a/docs/static/img/twobirds.jpg b/docs/static/img/twobirds.jpg new file mode 100644 index 0000000..6f3ca8a Binary files /dev/null and b/docs/static/img/twobirds.jpg differ diff --git a/docs/static/img/unreal.png b/docs/static/img/unreal.png new file mode 100644 index 0000000..727fa1f Binary files /dev/null and b/docs/static/img/unreal.png differ diff --git a/docs/static/img/white.jpg b/docs/static/img/white.jpg new file mode 100644 index 0000000..dd262ee Binary files /dev/null and b/docs/static/img/white.jpg differ diff --git a/docs/static/img/world-map.jpg b/docs/static/img/world-map.jpg new file mode 100644 index 0000000..878f8fc Binary files /dev/null and b/docs/static/img/world-map.jpg differ diff --git a/docs/static/img/wtf-x-plus-one.png b/docs/static/img/wtf-x-plus-one.png new file mode 100644 index 0000000..3167243 Binary files /dev/null and b/docs/static/img/wtf-x-plus-one.png differ diff --git a/docs/static/js/anim.js b/docs/static/js/anim.js new file mode 100644 index 0000000..06bcab3 --- /dev/null +++ b/docs/static/js/anim.js @@ -0,0 +1,26 @@ +function swizzle(name){ + $(function() { + var divName = "#" + name; + var animName = "/liquidhaskell-blog/static/img/" + name + ".gif"; + var statName = "/liquidhaskell-blog/static/img/" + name + ".png"; + $(divName).hover( + function() { + $(this).attr("src", animName); + }, + function() { + $(this).attr("src", statName); + } + ); + }); +}; + +var z0 = swizzle("splash-head"); +var z1 = swizzle("splash-unstutter"); +var z2 = swizzle("splash-vectorsum"); +var z3 = swizzle("splash-dotproduct"); +var z4 = swizzle("splash-fib"); +var z5 = swizzle("splash-merge"); +var z6 = swizzle("splash-ups"); +var z7 = swizzle("splash-insertsort"); +var z8 = swizzle("splash-assocthm"); +var z9 = swizzle("splash-assocpf"); diff --git a/docs/static/js/bootstrap.min.js b/docs/static/js/bootstrap.min.js new file mode 100644 index 0000000..d839865 --- /dev/null +++ b/docs/static/js/bootstrap.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v3.3.1 (http://getbootstrap.com) + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.1",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.1",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active"));a&&this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.1",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c="prev"==a?-1:1,d=this.getItemIndex(b),e=(d+c)%this.$items.length;return this.$items.eq(e)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i="next"==b?"first":"last",j=this;if(!f.length){if(!this.options.wrap)return;f=this.$element.find(".item")[i]()}if(f.hasClass("active"))return this.sliding=!1;var k=f[0],l=a.Event("slide.bs.carousel",{relatedTarget:k,direction:h});if(this.$element.trigger(l),!l.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var m=a(this.$indicators.children()[this.getItemIndex(f)]);m&&m.addClass("active")}var n=a.Event("slid.bs.carousel",{relatedTarget:k,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),j.sliding=!1,setTimeout(function(){j.$element.trigger(n)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(n)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&"show"==b&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a(this.options.trigger).filter('[href="#'+b.id+'"], [data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.1",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0,trigger:'[data-toggle="collapse"]'},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.find("> .panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":a.extend({},e.data(),{trigger:this});c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){b&&3===b.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=c(d),f={relatedTarget:this};e.hasClass("open")&&(e.trigger(b=a.Event("hide.bs.dropdown",f)),b.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f)))}))}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.1",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('