GithubHelp home page GithubHelp logo

ucsd-progsys / liquidhaskell-tutorial Goto Github PK

View Code? Open in Web Editor NEW
72.0 9.0 28.0 14.31 MB

Tutorial for LiquidHaskell

Home Page: https://ucsd-progsys.github.io/liquidhaskell-tutorial/

License: MIT License

Makefile 0.02% Haskell 2.31% TeX 3.76% Liquid 0.01% Shell 0.02% CSS 0.41% JavaScript 93.45% Nix 0.01%

liquidhaskell-tutorial's Introduction

LiquidHaskell Tutorial

TODO: UPDATE the website with the new code

NOTE The PDF/HTML are sometimes not up-to-date with the latest LiquidHaskell release. Please clone the github repository and run locally for best results.

How to Do The Tutorial

LH is available as a GHC plugin from version 0.8.10.

Thus, the best way to do this tutorial is to

Step 1 Clone this repository,

$ git clone https://github.com/ucsd-progsys/liquidhaskell-tutorial.git

Step 2: Iteratively edit-compile until it builds without any liquid type errors

$ cabal v2-build

or

$ stack build --fast --file-watch

The above workflow will let you use whatever Haskell tooling you use for your favorite editor, to automatically display LH errors as well.

Contents

Part I: Refinement Types

  1. Introduction
  2. Logic & SMT
  3. Refinement Types
  4. Polymorphism
  5. Refined Datatypes

Part II: Measures

  1. Boolean Measures
  2. Numeric Measures
  3. Set Measures

Part III : Case Studies

  1. Case Study: Okasaki's Lazy Queues
  2. Case Study: Associative Maps
  3. Case Study: Pointers & Bytes
  4. Case Study: AVL Trees

Update

$ git pull origin master
$ git submodule update --recursive

Building

Deploy on Github

$ cp package.yaml.pandoc package.yaml
$ mkdir _site dist
$ stack install pandoc
$ make html
$ make pdf
$ cp dist/pbook.pdf _site/book.pdf
$ git add _site
$ git commit -a -m "updating GH-PAGES"
$ git push --force-with-lease origin HEAD:gh-pages

Prerequisites

  • Install LaTeX dependencies:
    • Texlive
    • texlive-latex-extra
    • texlive-fonts-extra

Producing .pdf Book

$ make pdf
$ evince dist/pbook.pdf

Solutions to Exercises

Solutions are in separate private repo

TODO

A work list of TODO items can be found in the bug tracker

Feedback and Gotchas

Editing feedback and various gotchas can be found in feedback.md

liquidhaskell-tutorial's People

Contributors

adinapoli avatar alanz avatar bandali0 avatar catarinagamboa avatar christetreault avatar crabmusket avatar dijkstracula avatar f7deleon avatar facundominguez avatar gridaphobe avatar mmport80 avatar nikivazou avatar pseudonom avatar rakeshgk avatar ranjitjhala avatar robkorn avatar shingarov avatar skyzh avatar tjade273 avatar waddlaw avatar y-taka-23 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

liquidhaskell-tutorial's Issues

liquidhaskell error about merge example

Liquidhaskell reports an error with one of the examples:

 /home/amy/liquidhaskell-tutorial/src/05-datatypes.lhs:338:1-5: Error: Liquid Type Mismatch
  
 338 | merge xs  Emp = xs
       ^^^^^
  
   Inferred type
     VV : {v : GHC.Prim.Addr# | v == "/home/amy/liquidhaskell-tutorial/src/05-datatypes.lhs:(338,1)-(342,39)|function merge"}
  
   not a subtype of Required type
     VV : {VV : GHC.Prim.Addr# | 5 < 4}

The code it's complaining about is this:

merge         :: (Ord a) => IncList a -> IncList a -> IncList a
merge xs  Emp = xs    <--- line 338
merge Emp ys  = ys
merge (x :< xs) (y :< ys)
  | x <= y    = x :< merge xs (y :< ys)
  | otherwise = y :< merge (x :< xs) ys

"not a subtype of Required type" error with insertSort' exercise

In chapter 5, there's a trivially solvable exercise to specify the insertSort' function using foldr.

The intended solution is presumably that f = insert and b = Emp.

But if I "inline" the insert function without a type signature, it doesn't work:

insertSort'     :: (Ord a) => [a] -> IncList a
insertSort' xs  = foldr f b xs
  where
     f y Emp       = y :< Emp
     f y (x :< xs)
       | y <= x         = y :< x :< xs
       | otherwise      = x :< f y xs
     b             = Emp

Again, adding a type signature for f helps:

     f             :: (Ord a) => a -> IncList a -> IncList a

Could you explain why it's necessary to add this type signature?

Can't get tutorial up and running

"stack install" in the repo directory gives me:

...
The current entry points to /home/marius/projekte/haskell/liquidhaskell-tutorial/liquidhaskell/ but no .cabal file could be found there.

and if I execute "stack exec -- liquid ./src/01-intro.lhs" afterwards, I get

Executable named liquid not found on path

What am I doing wrong?

Chapter 2 Issues

There are some examples in Chapter 2 that is supposed to be safe but turns out it isn't. First I thought that it was only on my machine but it turns out it's deemed unsafe in the online demo too.

First, examples that really shouldn't be safe because of writer's logic error :
{-@ ex7 :: Bool -> Bool -> TRUE @-}
ex7 a b = a ==> (a ==> b) ==> b

{-@ ax3 :: Int -> Int -> TRUE @-}
ax3 x y = (0 <= x) ==> (0 <= y) ==> (0 <= x + y)

{-@ ax5 :: Int -> Int -> Int -> TRUE @-}
ax5 x y z = (x <= 0 && x >= 0)
==> (y == x + z)
==> (y == z)

They only work if one replace the first ==> with && because all of them are False if all parameters are False.

Second, almost all uninterpreted function example is somehow deemed unsafe by SMT Solver except fx0. It's very perplexing because it means somehow the entire section is wrong for some reason.

Issues with LaTeX styling

I'm opening this issue to bring up styling/formatting related things that I feel could use improvement. Strictly speaking, this is all opinion. I plan to add things to the comments section as I notice them, but here are a few right off the bat:

\newthought{I like cheetos}

heading

I feel that the text inside the newthought block looks to similar to regular text in the document. Sure, if you look closely you can see that the font is slightly different, and it's all capitals, but if you're skimming the document it all looks exactly the same. Perhaps these should be bold?

spec vs code blocks

spec_vs_code

Throughout the document there are ~~~~~{.spec} ... ~~~~~ blocks and \begin{code} ... \end{code} blocks. I understand that the latter has specific meaning for literate haskell, so we can't just use code blocks throughout, however I think the former needs to look more like the latter. I feel like ~~~~~{.spec} ... ~~~~~ used to make the enclosed text appear in a gray box, and I think that's how it should work. I suspect this change might be in response to an issue I raised earlier where text spilled out of the spec block. I think a better solution to that specific issue would be to break offending text onto multiple lines.

Ch 4: Cannot unify Data.Vector.Vector with Data.Vector.Generic.Base.Vector

Hi, I can't get VectorN to work for twoLangs in the example from chapter 4.

Here's a condensed version of my code:

{-@ LIQUID "--reflection" @-}

module Blaze.Liquid.Ex4 where

import Prelude hiding (abs)
import Data.Vector (Vector, (!))
import qualified Data.Vector as V

{-@ type VectorN a N = {v:Vector a | vlen v == N} @-}

{-@ twoLangs :: VectorN String 2 @-}
twoLangs  = V.fromList ["haskell", "javascript"]

and here's the error I get:

Error: Specified type does not refine Haskell type for `Blaze.Liquid.Ex4.twoLangs` (Checked)
  
 53 | {-@ twoLangs :: VectorN String 2 @-}
                      ^^^^^^^^^^^^^^^^^
  
  
 The Liquid type
  
     Data.Vector.Generic.Base.Vector [GHC.Types.Char]
  
 is inconsistent with the Haskell type
  
     Data.Vector.Vector [GHC.Types.Char]
  
 defined at /home/matt/haskell/blaze/src/Blaze/Liquid/Ex4.hs:55:1-8
  


 /home/matt/haskell/blaze/src/Blaze/Liquid/Ex4.hs:53:17-33: Error: Illegal type specification for `Blaze.Liquid.Ex4.twoLangs`
  
 53 | {-@ twoLangs :: VectorN String 2 @-}
                      ^^^^^^^^^^^^^^^^^
  
     Blaze.Liquid.Ex4.twoLangs :: {VV : (Data.Vector.Generic.Base.Vector [GHC.Types.Char]) | vlen VV == 2}
     Sort Error in Refinement: {VV##0 : (Data.Vector.Generic.Base.Vector [Char]) | vlen VV##0 == 2}
     Cannot unify Data.Vector.Vector with Data.Vector.Generic.Base.Vector in expression: vlen VV##0 

I tried importing Vector from Data.Vector.Generic.Base but that didn't work either.

I'm running liquid haskell with the command:

stack exec -- liquid src/Blaze/Liquid/Ex4.hs

Request Failed0

This code fails to compile (stuck at "Verifying...") and gives "Request Failed0" instead. Is there anything wrong with the code?

PS: While I'm at it, I'm only able to do the quickSort assignment because I'm using assume (++). Is it really the intended way?

Difference between "null elts" and "length elts == 0"

So I tried to solve the Sanitization exercise is Chapter 5 and my initial solution i
fromList :: Int -> [(Int, a)] -> Maybe (Sparse a)
fromList dim elts = if testList dim elts then Just (SP dim elts) else Nothing
where
testList dim elts
| dim < 0 = False
| null elts = True
| fst(head(elts)) < 0 = False
| fst(head(elts)) < dim = testList dim (tail elts)
| otherwise = False

but it failed according to Liquid Haskell. So I tried to change it to
fromList :: Int -> [(Int, a)] -> Maybe (Sparse a)
fromList dim elts = if testList dim elts then Just (SP dim elts) else Nothing
where
testList dim elts
| dim < 0 = False
| length elts == 0 = True
| fst(head(elts)) < 0 = False
| fst(head(elts)) < dim = testList dim (tail elts)
| otherwise = False

And it immediately succeed. I suspect that Liquid Haskel can't recognize null so I tried to add {-@ measure null @-} but it says "cannot extract measure from Haskell function".

So why does this happen? Shoud I never use null? I still don't really understand which built-in function I can use to reason in Liquid Haskell and which is not. Which built-in function I can use measure with is still ambiguous in general.

Why does this zipWith from Chapter 7 fails?

I'm trying the codes from Chapter 7 in this code and somehow here all the zip functions fail. Is there anything missing that would make these functions safe?

Additional Note: I also have to comment out the DotProd function because it uses a different zipWith with the one that is written later in the code hence it will fail since built-in zipWith doesn't need its parameters to have the same size.

Chapter 3: list average function causes no errors

Hello. I'm reading chapter 3, and I may find a problem with it.

At list average problem, it says avg caused a LiquidHaskell error. But it doesn't.

src$ liquid 03-basic.lhs
(omit)
**** RESULT: UNSAFE ************************************************************

 03-basic.lhs:70:1-4: Error: Liquid Type Mismatch
 70 | one' = 1 :: Int
      ^^^^
 03-basic.lhs:393:23-47: Error: Liquid Type Mismatch
 393 | lAssert False _ = die "yikes, assertion fails!"
                             ^^^^^^^^^^^^^^^^^^^^^^^^^

However it fails successfully when copy and paste avg from chapter 3.

$ liquid avg.hs
(omit)
**** RESULT: UNSAFE ************************************************************

 avg.hs:15:10-23: Error: Liquid Type Mismatch
 15 | avg xs = divide total n
               ^^^^^^^^^^^^^^

Why does this difference occur? All logs is in this gist. I use LiquidHaskell-6.0.0 (precisely, I install LiquidHaskell from source at the point of May 4, 2016) and GHC 7.10.3.

Nothing to fix in exercise (Average, Maybe), chapter 6

The following exercise in chapter 6, seems to work fine, and liquidhaskell marks it as safe. So it's unclear what there is to fix.

average'      :: [Int] -> Maybe Int
average' xs
  | ok        = Just $ divide (sum xs) elems 
  | otherwise = Nothing 
  where
    elems     = size xs
    ok        = elems > 0 -- What expression goes here? 

Was ok supposed to be undefined?

Which description is correct?

Question: expression and predicate syntax.

Readme in liquidhaskell repo

expression

e := v                      -- variable
   | c                      -- constant
   | (e + e)                -- addition
   | (e - e)                -- subtraction
   | (c * e)                -- multiplication by constant
   | (v e1 e2 ... en)       -- uninterpreted function application
   | (if p then e else e)   -- if-then-else

predicate

p := (e r e)          -- binary relation
   | (v e1 e2 ... en) -- predicate (or alias) application
   | (p && p)         -- and
   | (p || p)         -- or
   | (p => p)         -- implies
   | (not p)          -- negation
   | true
   | false

But, this tutorial is below:

expression

    e := v                   -- variable
       | c                   -- constant
       | e + e               -- addition
       | e - e               -- subtraction
       | c * e               -- linear multiply
       | v e1 e2 ... en      -- uninterpreted function application

predicate

    p := true
       | false
       | e r e           -- atomic binary relation
       | v e1 e2 ... en  -- predicate application
       | p  && p         -- and
       | p  || p         -- or
       | p ==> p         -- implies
       | p <=> p         -- if and only if
       | not p           -- negation

Both of a bit different. Which is correct?

divide function in Chapter 3

I have copied and pasted this code from Chapter 3. I believe it should be OK, but LiquidHaskell doesn't like it. Have I misunderstood something, or is this a bug in the tutorial or in LiquidHaskell?

BTW, thank you for the tutorial. It's well-written.

{-@ LIQUID "--no-termination" @-}

{-@ type NonZero = {v:Int | v /= 0} @-}

{-@ die :: {v:String | false} -> a  @-}
die msg = error msg

{-@ divide :: Int -> NonZero -> Int @-}
divide _ 0 = die "divide by zero"
divide n d = n `div` d
$ liquid amy2.hs
LiquidHaskell Version 0.8.4.0, Git revision 57213512a9d69093c12d644b21dbf9da95811894
Copyright 2013-18 Regents of the University of California. All Rights Reserved.


**** DONE:  A-Normalization ****************************************************                                             
                                                                                                                             
                                                                                                                             
**** DONE:  annotate ***********************************************************                                             
                                                                                                                             
                                                                                                                             
**** RESULT: ERROR *************************************************************                                             
                                                                                                                             

 /home/amy/liquidhaskell-tutorial/amy2.hs:8:15-36: Error: Specified type does not refine Haskell type for `Main.divide` (Plugged Init types old)
  
 8 | {-@ divide :: Int -> NonZero -> Int @-}
                   ^^^^^^^^^^^^^^^^^^^^^^
  
  
 The Liquid type
  
     GHC.Types.Int
     -> GHC.Types.Int
        -> GHC.Types.Int
  
 is inconsistent with the Haskell type
  
     forall p -> GHC.Real.Integral p => p -> p -> p
  
 defined at /home/amy/liquidhaskell-tutorial/amy2.hs:9:1-6
  
 Specifically, the Liquid component
  
     GHC.Types.Int
  
 is inconsistent with the Haskell component
  
     p
  
 
 HINT: Use the hole '_' instead of the mismatched component (in the Liquid specification)

LaTeX .sty files missing

When building the book, various .sty files are missing. I've been downloading them one-by-one until make stops failing, but I've downloaded 11 so far, and there is no end in sight.

Is there a list of LaTeX packages required for the book to build? Is there some sort of combined package or distribution that I can download that has all the dependencies? Failing that, can we get the .sty files added to the repository (barring licensing issues), so that we can just call make and have it work?

Feedback RE: updates to asides in chapter 1

@ranjitjhala I've finished proofreading chapter 1 again. Before I submit my pull request, I'd like to talk about a few of the asides:

RE: Aside 1

I don't believe that this argument holds water. If we were to code
more defensively using a Maybe, then we'd completely avoid this crash;
assuming you didn't do something silly like:

maybeAverage :: [Int] -> Maybe Int
maybeAverage l = Just $ average l

... which would just kick the can down the road. However, if we did something
like this:

maybeAverage' :: [Int] -> Maybe Int
maybeAverage' [] = Nothing
maybeAverage' l = Just $ average l

... then we'd be good. I believe a better argument would be against the code
bloat associated with this, and the cost of unwraping the Maybe. I propose the following replacement:

We could write `average` more *defensively*, returning a `Maybe` or `Either`
value. However, this added safety comes with a price. The caller of this new
`average` function must write additional code to unwrap the optional type,
adding considerable amounts of code that may be unnecessary if it is easy to
guarantee that the empty list will never be passed into `average`. Additionally,
the overhead of always performing this check may be unacceptable in high
performance code.

RE: Aside 2

Same as before. Proposed change:

Again, one could use a `Maybe`, but as before they would just be trading
one problem for another.

If you are fine with my updates, I will implement them and submit the pull request. If not, I'll submit the pull request with the original wording.

Trouble building HTML of tutorial

I have finally gotten around to working on this issue ucsd-progsys/liquidhaskell#1345 but I wanted to build the HTML docs first.

I've followed the instructions but am running into this error

make html
stack exec -- toc src/ templates/pagemeta.template templates/index.template dist/page.template dist/index.lhs dist/links.txt
Executable named toc not found on path: ["/home/jesse/projects/liquidhaskell-tutorial/.stack-work/install/x86_64-linux/lts-10.3/8.2.2/bin","/home/jesse/.stack/snapshots/x86_64-linux/lts-10.3/8.2.2/bin","/home/jesse/.stack/compiler-tools/x86_64-linux/ghc-8.2.2/bin","/home/jesse/.stack/programs/x86_64-linux/ghc-8.2.2/bin","/home/jesse/.rbenv/shims","/home/jesse/.rbenv/bin","/home/jesse/.local/bin","/usr/local/sbin","/usr/local/bin","/usr/sbin","/usr/bin","/sbin","/bin","/usr/games","/usr/local/games","/snap/bin"]
Makefile:82: recipe for target 'dist/index.lhs' failed
make: *** [dist/index.lhs] Error 1

After sifting through the make file I found the culprit line I think:

INDEXER=stack exec -- toc

I'm not really sure where the toc command comes from and my google searches were fruitless. Can you help me out here? On the bright side, the pdf builds fine!

Arithmetic example 4 doesn't work in "Logic & SMT"

Code from the tutorial:

{-@ ax3 :: Int -> Int -> TRUE @-}
ax3 :: Int -> Int -> Bool
ax3 x y = (0 <= x) ==> (0 <= y) ==> (0 <= x + y)

LiquidHaskell response:

   67 | ax3 x y = (0 <= x) ==> (0 <= y) ==> (0 <= x + y)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

   Inferred type
     VV : {v : GHC.Types.Bool | v <=> ((0 <= x => 0 <= y) => 0 <= x + y)}

   not a subtype of Required type
     VV : {VV : GHC.Types.Bool | VV}

   In Context
     x : GHC.Types.Int

     y : GHC.Types.Int
  • GHC: 8.6.3
  • Liquid: LiquidHaskell Version 0.8.4.0, Git revision 5721351 (dirty)

What was the original intention? For example the code below passes:

ax3 x y = (x >= 0 && y >= 0) ==> (x + y >= 0)

Error: errs is undefined (Polymorphism section)

Going through the tutorial in the github pages, I get the following error on the polymorphism page:

Error: errs is undefined
blockErrors@http://ucsd-progsys.github.io/liquidhaskell-tutorial/js/liquid/liquid.js:198:5
setErrors@http://ucsd-progsys.github.io/liquidhaskell-tutorial/js/liquid/liquid.js:207:19
verifyQuery/<@http://ucsd-progsys.github.io/liquidhaskell-tutorial/js/liquid/liquid.js:415:38
$http/promise.success/<@http://ucsd-progsys.github.io/liquidhaskell-tutorial/js/angular/angular.js:8663:11
wrappedCallback@http://ucsd-progsys.github.io/liquidhaskell-tutorial/js/angular/angular.js:6623:31
then/<@http://ucsd-progsys.github.io/liquidhaskell-tutorial/js/angular/angular.js:6660:26
$eval@http://ucsd-progsys.github.io/liquidhaskell-tutorial/js/angular/angular.js:7808:16
$digest@http://ucsd-progsys.github.io/liquidhaskell-tutorial/js/angular/angular.js:7680:17
$apply@http://ucsd-progsys.github.io/liquidhaskell-tutorial/js/angular/angular.js:7894:13
done@http://ucsd-progsys.github.io/liquidhaskell-tutorial/js/angular/angular.js:8883:9
completeRequest@http://ucsd-progsys.github.io/liquidhaskell-tutorial/js/angular/angular.js:9023:7
createHttpBackend/</xhr.onreadystatechange@http://ucsd-progsys.github.io/liquidhaskell-tutorial/js/angular/angular.js:8993:11

The error comes up when I try to run a code sample. In liquid.js, inside LiquidDemoCtrl there's a verifyQuery function which calls setErrors with undefined for the 2nd param:

$scope.errorBlocks = setErrors(blocks, data.errors); // line 207 online

The error prevents running the code samples and seeing the annotations.

I'm on Firefox 62

The project contains no local packages (packages not marked with 'extra-dep')

Following the instructions in the README; stack build fails.

$ git clone --recursive https://github.com/ucsd-progsys/liquidhaskell-tutorial.git
Cloning into 'liquidhaskell-tutorial'...
remote: Enumerating objects: 1911, done.
remote: Total 1911 (delta 0), reused 0 (delta 0), pack-reused 1911
. . .
$ cd liquidhaskell-tutorial/
$ stack install
Error parsing targets: The project contains no local packages (packages not marked with 'extra-dep')

Stack used to allow you to have a stack.yaml where all of the packages were external, but it no longer does. One possible workaround is to have a trivial local package ("Hello, Liquid Haskell").

Merge example (05-datatypes) won't compile

This may be related to issue #76, but the (temporary?) workaround listed there didn't help.

Compiling the merge example on the website produces a different error message about totality, although maybe it is relevant too. I tried invoking Liquid with the following code:

{-@ data IncList a = Emp
                | (:<) { hd :: a, tl :: IncList {v:a | hd <= v}}  @-}
data IncList a =
    Emp
  | (:<) { hd :: a, tl :: IncList a }
  deriving (Show, Eq)

infixr 9 :<

{-@ merge             :: (Ord a) => IncList a -> IncList a -> IncList a @-}
merge :: (Ord a) => IncList a -> IncList a -> IncList a
merge xs  Emp = xs
merge Emp ys  = ys
merge a@(x :< xs) b@(y :< ys)
  | x <= y    = x :< merge xs b
  | otherwise = y :< merge a ys
merge _ _ = Emp

I got this output:

$ liquid --no-termination --no-totality lh-ch5-2.hs

LiquidHaskell Version 0.8.6.0, Git revision 6f29ece8f61483f0546d052308882c8abef6da78 (dirty) [develop@6f29ece8f61483f0546d052308882c8abef6da78 (Fri Apr 17 19:03:44 2020 +0200)] 
Copyright 2013-19 Regents of the University of California. All Rights Reserved.

Targets: lh-ch5-2.hs

**** DONE:  A-Normalization ****************************************************
**** DONE:  Extracted Core using GHC *******************************************
**** DONE:  Transformed Core ***************************************************
**** DONE:  annotate ***********************************************************

**** RESULT: UNSAFE ************************************************************
 /home/omf/code/haskell/liquid/ex/lh-ch5-2.hs:15:22-31: Error: Liquid Type Mismatch
  
 15 |   | x <= y    = x :< merge xs b
                           ^^^^^^^^^^
  
   Inferred type
     VV : a
  
   not a subtype of Required type
     VV : {VV : a | x <= VV}
  
   In Context
     x : a


 /home/omf/code/haskell/liquid/ex/lh-ch5-2.hs:16:22-31: Error: Liquid Type Mismatch
  
 16 |   | otherwise = y :< merge a ys
                           ^^^^^^^^^^
  
   Inferred type
     VV : a
  
   not a subtype of Required type
     VV : {VV : a | y <= VV}
  
   In Context
     y : a

Shouldn't it infer in these cases that if x <= y, then merge xs (y :< ys) must contain only elements greater or equal to x? Apologies if this is an obvious understanding fail on my part.

Ch 5: Sparse's 'fromList' works even if it just returns Nothing

I have the Sparse datatype and I made a fromList conversion function (named sparseFromList) that works in Haskell and passes the check with Liquid Haskell:

data Sparse a = SP { spDim   :: Int
                   , spElems :: [(Int, a)] }
                deriving (Eq, Ord, Show)

{-@ data Sparse a = SP { spDim :: Nat, spElems :: [(Btwn 0 spDim, a)] } @-}

{-@ type SparseN a N = {sp:Sparse a | spDim sp == N} @-}

sparseFromList :: Int -> [(Int, a)] -> Maybe (Sparse a)
sparseFromList dim elts = SP dim <$> traverse f elts
  where
    f (i, x)
      | i >= 0 && i < dim = Just (i, x)
      | otherwise = Nothing

{-@ test1 :: SparseN String 3 @-}
test1     = fromJust $ sparseFromList 3 [(0, "cat"), (2, "mouse")]

But then, just to make sure it was actually checking sparseFromList, I made it always return Nothing and LH still said it was OK.

Then I decided to make my own fromJust instead of importing it, with a LH check that it's actually a Just:

{-@ measure isJust @-}
isJust :: Maybe a -> Bool
isJust (Just _) = True
isJust Nothing = False

{-@ fromJust :: {m:Maybe a | isJust m} -> a @-}
fromJust :: Maybe a -> a
fromJust (Just a) = a
fromJust Nothing = die "fromJust Nothing"

And now I get a LH error that makes it look like it can't figure out that sparseFromList returns a Just in this case.

197 | test1     = fromJust $ sparseFromList 3 [(0, "cat"), (2, "mouse")]
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  
   Inferred type
     VV : (GHC.Maybe.Maybe (Blaze.Liquid.Ex5.Sparse [GHC.Types.Char]))
  
   not a subtype of Required type
     VV : {VV : (GHC.Maybe.Maybe (Blaze.Liquid.Ex5.Sparse [GHC.Types.Char])) | isJust VV}

Issues with quickSort in chapter 6

Hello! I encountered some problems when I was trying solving exercise 6.6

Exercise 6.6 (QuickSort). Use partition to implement quickSort.
-- >> quickSort [1,4,3,2] 
-- [1,2,3,4] 
{-@ quickSort :: (Ord a) => xs:List a -> ListX a xs @-} 
quickSort [] = [] 
quickSort (x:xs) = undefined 
{-@ test10 :: ListN String 2 @-} 
test10 = quickSort test4

Here's my implementation:

{-@ partition :: _ -> xs:_ -> {v:_ | Sum2 v (size xs)} @-}
{-@ predicate Sum2 X N = size (fst X) + size (snd X) = N @-}

partition :: (a -> Bool) -> [a] -> ([a], [a])
partition _ [] = ([], [])
partition f (x:xs)
  | f x = (x:ys, zs)
  | otherwise = (ys, x:zs)
  where
    (ys, zs) = partition f xs

-- exercise 6.6
{-@ quickSort' :: (Ord a) => xs:List a -> ListX a xs @-}
quickSort' :: (Ord a) => [a] -> [a]
quickSort' [] = []
quickSort' (x:xs) = let
  (l, r) = partition cmp xs
  cmp t = t < x in
     quickSort' l ++ [x] ++ quickSort' r

Which I got the following error:

 /Users/skyzh/Work/playground/src/Ex06.hs:152:6-17: Error: Liquid Type Mismatch

 152 |      quickSort' l ++ [x] ++ quickSort' r
            ^^^^^^^^^^^^

   Inferred type
     VV : {v : [a##xo] | len v >= 0
                         && v == fst ?a
                         && v /= ?b
                         && Ex06.size v >= 0}

   not a subtype of Required type
     VV : {VV : [a##xo] | len VV < len ?b
                          && len VV >= 0}

   In Context
     xs : {v : [a##xo] | Ex06.size v >= 0
                         && len v >= 0}

     ?b : {?b : [a##xo] | Ex06.size ?b >= 0
                          && len ?b >= 0}

     x : a##xo

     ?a : {?a : ([a##xo], [a##xo]) | Ex06.size (fst ?a) + Ex06.size (snd ?a) == Ex06.size xs}


 /Users/skyzh/Work/playground/src/Ex06.hs:152:6-40: Error: Liquid Type Mismatch

 152 |      quickSort' l ++ [x] ++ quickSort' r
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

   Inferred type
     VV : {v : [a##xo] | len v == len ?f + len ?b
                         && Ex06.size v >= 0
                         && len v >= 0}

   not a subtype of Required type
     VV : {VV : [a##xo] | Ex06.size VV == Ex06.size ?e}

   In Context
     ?g : {?g : [a##xo] | (Ex06.notEmpty ?g <=> false)
                          && Ex06.size ?g == 0
                          && len ?g == 0
                          && Ex06.size ?g >= 0
                          && len ?g >= 0}

     xs : {v : [a##xo] | Ex06.size v >= 0
                         && len v >= 0}

     ?f : {?f : [a##xo] | Ex06.size ?f == Ex06.size (fst ?c)
                          && Ex06.size ?f >= 0
                          && len ?f >= 0}

     ?c : {?c : ([a##xo], [a##xo]) | Ex06.size (fst ?c) + Ex06.size (snd ?c) == Ex06.size xs}

     ?e : {?e : [a##xo] | Ex06.size ?e >= 0
                          && len ?e >= 0}

     ?b : {?b : [a##xo] | len ?b == len ?a + len ?d
                          && Ex06.size ?b >= 0
                          && len ?b >= 0}

     x : a##xo

     ?d : {?d : [a##xo] | Ex06.size ?d == Ex06.size (snd ?c)
                          && Ex06.size ?d >= 0
                          && len ?d >= 0}

     ?a : {?a : [a##xo] | tail ?a == ?g
                          && head ?a == x
                          && (Ex06.notEmpty ?a <=> true)
                          && Ex06.size ?a == 1 + Ex06.size ?g
                          && len ?a == 1 + len ?g
                          && Ex06.size ?a >= 0
                          && len ?a >= 0}


 /Users/skyzh/Work/playground/src/Ex06.hs:152:29-40: Error: Liquid Type Mismatch

 152 |      quickSort' l ++ [x] ++ quickSort' r
                                   ^^^^^^^^^^^^

   Inferred type
     VV : {v : [a##xo] | len v >= 0
                         && v == snd ?a
                         && v /= ?b
                         && Ex06.size v >= 0}

   not a subtype of Required type
     VV : {VV : [a##xo] | len VV < len ?b
                          && len VV >= 0}

   In Context
     xs : {v : [a##xo] | Ex06.size v >= 0
                         && len v >= 0}

     ?b : {?b : [a##xo] | Ex06.size ?b >= 0
                          && len ?b >= 0}

     x : a##xo

     ?a : {?a : ([a##xo], [a##xo]) | Ex06.size (fst ?a) + Ex06.size (snd ?a) == Ex06.size xs}

And I suspect that Liquid Haskell may fail to deduce the size of operator ++. Then I made an experiment to test.

{-@ (+++) :: xs: List a -> ys: List a -> ListN a {size xs + size ys} @-}
(+++) :: [a] -> [a] -> [a]
a +++ b = a ++ b

And certainly I got the following error:

/Users/skyzh/Work/playground/src/Ex06.hs:143:11-16: Error: Liquid Type Mismatch

 143 | a +++ b = a ++ b
                 ^^^^^^

   Inferred type
     VV : {v : [a##xo] | len v == len a + len b
                         && Ex06.size v >= 0
                         && len v >= 0}

   not a subtype of Required type
     VV : {VV : [a##xo] | Ex06.size VV == Ex06.size a + Ex06.size b}

   In Context
     a : {a : [a##xo] | Ex06.size a >= 0
                        && len a >= 0}

     b : {b : [a##xo] | Ex06.size b >= 0
                        && len b >= 0}

Is there anyway to make Liquid Haskell deduce that ++ can correctly add up size?

Thank you for reading this issue!

Typos

I found the following minor issues when going through the web version of the tutorial:

Chapter 8, footnote 1, the link is broken. It should be: https://ucsd-progsys.github.io/liquidhaskell-blog/2013/03/26/talking-about-sets.lhs/

Chapter 8, the hint Hint: You may want to remind yourself about the dimension-aware signature for partition from the earlier chapter. needs to point to https://ucsd-progsys.github.io/liquidhaskell-tutorial/07-measure-int.html#/listreducing

The following example from chapter 8 is rejected with a rather unexpected warning (develop branch of liquidhaskell):

{-@ mergeSort :: (Ord a) => xs:List a -> ListEmp a @-}
mergeSort []  = []
mergeSort xs  = merge (mergeSort ys) (mergeSort zs)
  where
   (ys, zs)   = halve mid xs
   mid        = length xs `div` 2
**** RESULT: ERROR *************************************************************


 /Users/ethan/haskell/liquid/src/sets.hs:125:18: Error: Specified Type Does Not Refine Haskell Type for Sets.mergeSort

 125 | {-@ mergeSort :: (Ord a) => xs:List a -> ListEmp a @-}
                        ^

 The Liquid type

     forall t t. GHC.Classes.Ord t => [t] -> [t]

 is inconsistent with the Haskell type

     forall t t. GHC.Classes.Ord t => [t] -> [t]

 defined at /Users/ethan/haskell/liquid/src/sets.hs:126:1-9

But if I add an explicit Haskell type to this, it works:

{-@ mergeSort :: (Ord a) => xs:List a -> ListEmp a @-}
mergeSort :: (Ord a) => [a] -> [a]
mergeSort []  = []
mergeSort xs  = merge (mergeSort ys) (mergeSort zs)
  where
   (ys, zs)   = halve mid xs
   mid        = length xs `div` 2

I'm not sure if this is an error in liquid or a typo in the example

Chapter 2.3, ex3 and ex4 don't work because (==>) is not in scope as a haskell value.

The examples look like this:

{-@ ex3 :: Bool -> Bool -> TRUE @-}
ex3 a b = (a && b) ==> a

{-@ ex4 :: Bool -> Bool -> TRUE @-}
ex4 a b = (a && b) ==> b

When attempting to verify the first of those, I get:

**** ERROR: Invalid Source *****************************************************


 tutorial-ch2.hs:19:20-22: Error: GHC Error
     Not in scope: ‘==>’
Perhaps you meant ‘==’ (imported from Prelude)

What am I missing? Just adding the definition of (==>) from earlier in the chapter doesn't fix it. I end up getting:

**** UNSAFE ********************************************************************


 tutorial-ch2.hs:19:11-24: Error: Liquid Type Mismatch
   Inferred type
     VV : Bool

   not a subtype of Required type
     VV : Bool | Prop VV

   In Context
     VV : Bool

I assume this is because liquid haskell doesn't look inside definitions, and just uses Bool -> Bool -> Bool as the type for (==>) when it lacks an accurate liquid haskell annotation.

Type of `dropWhile`

The type of dropWhile could be more specific:

{-@ measure head :: forall a. { es : [a] | len es >= 1 } -> a @-}
{-@ assume head :: { es : [a] | len es >= 1 } -> { e : a | e == head es } @-}
{-@ assume dropWhile
    :: p : (a -> Bool)
    -> ls : [a]
    -> { o : [a] | len o <= len ls && len o >= 1 ==> not (Prop (p (head o))) }
@-}

This way we can prove the following safe:

main :: IO ()
main =
  if head (dropWhile ((==) 3) [1..3]) == 3
  then return ()
  else error "Not going to happen"

Unfortunately there are two problems:

  1. Liquid Haskell complains that es is unbound in the new liquid signature for head.
  2. == only works for Eq types, so I'm not sure how to specify e == head es.

Feedback/help is welcome.

Cleanup README.md

I've already cleaned up the formatting and flow of README.md quite a bit. Additionally, I added some info on building the .pdf. However, I am unsure of what to do about the TODO and Gotchas sections.

  • TODO
    This section is full of work items. Perhaps each work item should be opened as an issue?
  • Feedback and Gotchas
    This section seems to be full of feedback that I gave, as well as other random thoughts. There's not a lot of real organization here, and I'm sure a lot of this is not up to date. Maybe a wiki page can be created for this stuff?

I suppose both of those sections aren't really hurting anybody as-is, but I don't think they really belong in a readme file. Implementing both of my suggestions is probably only 15 minutes of work, and will go a long way towards making the readme feel more polished. Given that this repo is now public, I figure it couldn't hurt. Assuming management is OK with this, I can go ahead and make this change, then submit a pull request with the updated readme.

Do I need to do something to enable inference?

I'm looking at the loop example in chapter 4, and it says that LH will automatically infer the type type of loop to be:

loop :: lo:Nat -> hi:{Nat|lo <= hi} -> a -> (Btwn lo hi -> a -> a) -> a

for the function:

loop :: Int -> Int -> a -> (Int -> a -> a) -> a
loop lo hi base f =  go base lo
  where
    go acc i
      | i < hi    = go (f i acc) (i + 1)
      | otherwise = acc

But I have to manually put in the liquid type annotation or vectorSum' really won't work, and even then it actually still doesn't work because it doesn't infer the type of go.

My own loop function I made without a go works:

{-@ loop :: lo:Int -> {hi:Int | hi >= lo} -> a -> (Btwn lo hi -> a -> a) -> a @-}
loop :: Int -> Int -> a -> (Int -> a -> a) -> a
loop lo hi acc f
  | lo >= hi = acc
  | otherwise = loop (lo + 1) hi (f lo acc) f

The only two options I have on are:

{-@ LIQUID "--no-termination" @-}
{-@ LIQUID "--reflection" @-}

Also, is there a way to see the inferred types from the command line?

Why should 0 <= VV < acc

vectorSum :: Vector Int -> Int

The function vectorSum gives me a strange error:

 7 |    | i < sz = go (acc + (vec ! i)) (i + 1)
                       ^^^^^^^^^^^^^^^^^
  
   Inferred type
     VV : {v : GHC.Types.Int | v == acc + ?b}
  
   not a subtype of Required type
     VV : {VV : GHC.Types.Int | VV < acc
                                && VV >= 0}
  
   In Context
     ?b : GHC.Types.Int
      
     acc : GHC.Types.Int

As a re-check, the following code gives me the same error, when executed here

import Data.Vector
vectorSum         :: Vector Int -> Int
vectorSum vec     = go 0 0
  where
    go acc i
      | i < sz    = go (acc + (vec ! i)) (i + 1)
      | otherwise = acc
    sz            = Data.Vector.length vec

Chapter 4: how do included .spec files work?

In chapter 4, the use of external .spec files was mentioned: https://github.com/ucsd-progsys/liquidhaskell-tutorial/blob/master/src/04-poly.lhs#L93-L98

However, how this mechanism works is never explained. How does one include a .spec file? What rules are associated with these includes?

I've submitted a PR for my review of this chapter, but I consider this to be an outstanding issue.

If somebody can post a brief explanation for how this works, I'll see it get included in the book.

Completely lost in chapter 6

I've started work on chapter 6, but I am completely lost. I think it's probably a culmination of misunderstandings, but I feel that I won't be producing useful feedback if I press on. A non exhaustive list of issues:

The => operator: What does it do? It seems to be required to solve 6.1, but I can't figure out how to use it

Single = on the rhs: Specifications in chapter 6 do this a lot. How does this work? For a minute I began to wonder if I misremembered, and the equality operator was single = rather than double ==.

6.1 in general leaves me with the impression I've missed something big. It seems like I'd need to be able to assign a specification to each equation. How can I tell it that the first list should be empty, or the second list should be empty, or they should have the same length? How do I tell it which to use for which equation?

In general, a lot of syntaxes are being introduced, and not really explained. In chapter 6, the predicate keyword was used, and it seems obvious what it does. But I'm sure there's some subtlety to it that bears explanation.

Here is a link to what I've done so far. I'll probably restart this chapter, and hopefully it clicks next time.

https://drive.google.com/file/d/0Bx6BpheCXcBWVnVoQk11RkRsTUk/view?usp=sharing

TODO: Part IV and V and extra case studies

The following sections of the book remain to be written:


Part IV : Abstract Refinements (TODO)

  1. Abstract Refinements I (code)
    • FLOPS/IHP talk sequence
    • Vanilla/Code [compose, foldr, ...]
  2. Abstract Refinements II (data)
    • RecRef [List, BST]
    • Arrays
  3. Abstract Refinements III (bounds)
    • compose
    • filter
    • state

Part V: Tips and Tricks (TODO)

  1. Tips:
    • Inductive strengthening
    • Materializing Proofs
    • Assumes/Dynamic Checking
  2. Tricks: Totality
  3. Tricks: Termination
    • Copy from BLOG/PAPER sequence
    • HW Exercises

Extra Case Studies

  • Case Study 1: AlphaConvert (tests/pos/alphaconvert-List.hs)

  • Case Study 2: Kmeans

  • grep FIXME/TODO (!)

  • move HINT to ABOVE code block

  • Subtyping exercises

    • div by zero
    • array-bounds
    • create (bytestring)
  • LH fix

    • allow using CoreToLogic definitions (e.g. member) in
      predicates/aliases not just other measures #332
  • convert measure refinements into invariants, e.g.

    measure size :: [Int] -> Nat

    should yield invariant {v:[a] | 0 <= size v}

? Intelligible parse errors

  • Web demo

Confused by liquidhaskell submodule.

Hi,

I'm confused as to why you have liquidhaskell as a Git submodule, when it's not mentioned in either package.yaml or stack.yaml.

Also, is it required to use commit 91e1074 for some reason?

Thanks!

Cannot find Data.Vector

I get an error when trying the following. Any hints? The chapters until now are OK.

I cloning latest tutorial and liquid haskell from Github, running on Debian.

stack exec -- liquid src/04-poly.lhs
Downloaded lts-12.2 build plan.
Preparing to install GHC to an isolated location.
This will not interfere with any system-level installation.
Downloaded ghc-8.4.3.
Installed GHC.
LiquidHaskell Version 0.8.6.0, Git revision f4fe82c (dirty) [develop@f4fe82cd03fbe906379c8ebeac5ec3efae0b4cd8 (Mon Jun 24 10:55:17 2019 +0200)]
Copyright 2013-19 Regents of the University of California. All Rights Reserved.

Targets: src/04-poly.lhs

**** [Checking: src/04-poly.lhs] ***********************************************
liquid: VectorBounds: Could not find module ‘Data.Vector’
Perhaps you meant Data.Functor (from base-4.11.1.0)
Use -v to see a list of the files searched for.

Non-terminating sample code

Several pieces of code in the tutorial don't pass the termination checker. Should those bits of code be altered or should the tutorial explicitly mention --no-termination somewhere?

How do you refine a predicate which checks over all elements in a list?

I'm trying to write a specification for a function that checks over all elements in a list. For example what I'm trying to refine is a simple function like this

isOver :: Int -> [Int] -> Bool
isOver lo xs 
    | length xs == 0 = True
    | (lo < (head xs)) = isOver lo (tail xs)
    | otherwise = False

As can be seen isOver should be a function that checks whether all elements in the list are over some input and then returns True or False. It can of course also be implemented like this

isOver :: Int -> [Int] -> Bool
isOver lo = foldl (\acc x -> acc && (lo < x)) True 

How do I write a specification to check whether either implementation is safe? Or this kind of predicate cannot be written the specification and instead only assumed as true and be used as measure?

Tutorial doesn't build

I figured I'd build the new book, and restart following my issues with chapter 6. I cloned the repo and ran make which output:

$ make
pandoc --from=markdown+lhs --to=html5 -s --mathjax --standalone --parse-raw --mathjax --section-divs --filter web/templates/codeblock.hs --filter templates/html.hs --variable=notitle --highlight-style=tango --template=web/templates/page.template src/00-preamble.lhs src/03-poly.lhs src/99-bib.lhs -o web/dist/foo.html
pandoc: Could not find data file templates/web/templates/page.template
make: *** [site] Error 97

There is no web folder in templates, and the web in the project root is empty. On github, it links to another repository.

After it occurred to me that this was trying to build a webpage, and I want a pdf, I ran make book:

$ make book
cat src/00-preamble.lhs src/01-intro.lhs src/02-basic.lhs src/03-poly.lhs src/04-datatypes.lhs src/05-measure-bool.lhs src/06-measure-int.lhs src/07-measure-sets.lhs src/08-case-study-lazy-queues.lhs src/09-case-study-associative-maps.lhs src/10-case-study-pointers.lhs src/11-case-study-AVL.lhs src/99-bib.lhs > dist/pbook.lhs
/bin/sh: 1: cannot create dist/pbook.lhs: Directory nonexistent
make: *** [book] Error 2

If you create dist in the project root:

$ make book
cat src/00-preamble.lhs src/01-intro.lhs src/02-basic.lhs src/03-poly.lhs src/04-datatypes.lhs src/05-measure-bool.lhs src/06-measure-int.lhs src/07-measure-sets.lhs src/08-case-study-lazy-queues.lhs src/09-case-study-associative-maps.lhs src/10-case-study-pointers.lhs src/11-case-study-AVL.lhs src/99-bib.lhs > dist/pbook.lhs
pandoc --highlight-style=tango --from=markdown+lhs --biblio templates/sw.bib --chapters --latex-engine=pdflatex --template=templates/default.latex --filter templates/inside.hs dist/pbook.lhs -o dist/pbook.pdf
pandoc: Error running filter templates/inside.hs
templates/inside.hs not found in path
make: *** [book] Error 83

templates/inside.hs does exist, and giving it 777 permissions doesn't help. I looked into it a bit, but I can't figure out why it doesn't work. I noticed that there is a pre-built pdf in the repository already, so I'm going to move ahead with that. I just thought I'd share the issues I had with it, and what I found out.

Review of chapter 4 complete

I've completed my review of chapter 4, redlines can be found here:

https://drive.google.com/file/d/0Bx6BpheCXcBWMERKN2hEYUR4MWM/view?usp=sharing

I feel that I am being left behind in this. I had some difficulties in chapter 3, but nothing I couldn't overcome (or that couldn't be explained by a bug in the system). However, in this chapter, there were issues I was not able to work through. LH was complaining about things that I feel should be handled by the type definitions. I feel that I am missing some understanding about how LH validates things, and it's preventing me from effectively using it. Some exercises in this chapter, I had to use an ugly kludge to please the tool. Some I just called a loss.

I will press onward, but I am concerned that my lack of understanding is going to make things even more difficult moving forward.

A curious case with Where

I was trying to figure out how to make my code safe and then I try to isolate the problem when I stumbled upon a curious case.

I make this code and it's unsafe as expected. But then I try to tinker around the code and it results in this code which is safe! Eventhough aaaa actually doesn't change at all, adding an irrelevant function which just so happens to have an exact definition with the function in Where in aaaa makes it safe somehow.

I eventually found out that you need to make a specification also for the function defined in Where (in this case {-@ test :: (Ord a, Num a) => x:a -> {y:Bool | y <=> (x > 0)} @-}) to make the function passed safe but this particular case still seems like an odd behavior to me.

Tim's issues

  1. the bizarre length --> len suggestion
  2. what is (!) in chapter 4
  3. chapter 4 is too sudden:
    • what is a measure exactly
    • rapid introduction of "accumulator" loops e.g. vector sum/ higher-order functions
    • specifically Inference: Our First Recursive Function (is complex because it uses an accumulator)
    • then the HOF examples are very complex (and should be explained later after EASY HOFs)
  4. solve 3 by writing a small "measures" chapter between 3 and 4 which has lists,
    len, average, simple recursive functions on lists etc. basically -- convert chapter 3
    of lh-workshop into a simpler intermediate chapter with:
    (a) data types,
    (b) measures,
    (c) no recursion -- year, CSV
    (d) simple data-oriented recursion (e.g. map, append)
  5. The "measure" / Sec 2.6 bit in chapter 2 seems out of place, not really motivated well.
    Perhaps put into the 4 above and it would fit nicely.
  6. When you DO get into go style recursion with accumulators -- it is actually
    complex if the person has not seen it before, so more explanation there is helpful.
  7. Make clear what exactly is a DEFINITION (as in: enclose in a BOX explicitly
    marked as DEFINITION -- we are introducing a new keyword) e.g. "specification"
    (Ch 3 -- what IS a specification), or "verification" or "measures" (ch 4) or "assumes"
    -- when each such idea is introduced, mark it clearly as a new concept and give
    concrete examples.
  8. Perhaps reuse the "RefTypes = Types + Predicates" as the motif for introducing
    a new notion.
  9. For the more interesting examples -- have more discussion about
    • the right answer/ solution key
    • the other approaches
    • perhaps hidden behind a 'click here to discuss'
    • A nice example is the text around Exercise: (Vector Head) -- more of that would be valuable.
      +At the least, some solution to compare against.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.