ucsd-progsys / liquidhaskell-tutorial Goto Github PK
View Code? Open in Web Editor NEWTutorial for LiquidHaskell
Home Page: https://ucsd-progsys.github.io/liquidhaskell-tutorial/
License: MIT License
Tutorial for LiquidHaskell
Home Page: https://ucsd-progsys.github.io/liquidhaskell-tutorial/
License: MIT License
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}
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.
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}
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?
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.
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.
@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.
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?
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.
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.
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
?
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!
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.
I've completed my review of chapter 3 of the tutorial. The annotated .pdf can be found at:
https://drive.google.com/file/d/0Bx6BpheCXcBWaHJhdUxpTUNTbnc/view?usp=sharing
I had some issues with this chapter. Namely exercise 3.2, which I have concluded is impossible. I can only imagine that there must be some way to do it, but I couldn't find one (and I spent a good day or two banging my head against that wall).
Inference: Our First Recursive Function
(is complex because it uses an accumulator)lh-workshop
into a simpler intermediate chapter with:go
style recursion with accumulators -- it is actuallyExercise: (Vector Head)
-- more of that would be valuable.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
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:
liquidhaskell-tutorial/Makefile
Line 21 in 2913cc4
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!
In Chapter 7 (https://github.com/ucsd-progsys/liquidhaskell-tutorial/blob/master/src/07-measure-int.lhs#L136),
\begin{code}
{-@ measure size @-}
{-@ size :: xs:[a] -> {v:Nat | v = size xs} @-} -- Isn't it never needed?
size [] = 0
size (_:rs) = 1 + size rs
\end{code}
Because of mixed content when loaded over HTTPS.
The fix should be simple, just switch every loaded resource, aka stylesheet, JavaScript and images, to use HTTPS URLs.
For example on the introduction it looks like so:
The warning also shows up on this other URL, but there it looks fine but no idea why.
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
What was the original intention? For example the code below passes:
ax3 x y = (x >= 0 && y >= 0) ==> (x + y >= 0)
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?
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
liquidhaskell-tutorial/src/04-poly.lhs
Line 271 in c1e1b4f
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
The link in the readme 404s, and I could not find a similarly-named repo under ucsd-progsys
.
As it is, working through the tutorial is rather difficult without them.
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.
Question: expression
and predicate
syntax.
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
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:
e := v -- variable
| c -- constant
| e + e -- addition
| e - e -- subtraction
| c * e -- linear multiply
| v e1 e2 ... en -- uninterpreted function application
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?
I've finished my review of chapter 1, the annotated .pdf can be found at:
https://drive.google.com/file/d/0Bx6BpheCXcBWZzZRQkc4QjByZXM/view
Text in blue are just thoughts I had, nothing particularly critical.
Text in red are what I'd consider to be Real Issues.
There wasn't a lot of problems with chapter 1. Random extra apostraphe on page 12, a dead link on page 14, and issues using a solver that isn't z3.
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
?
The following sections of the book remain to be written:
Case Study 1: AlphaConvert (tests/pos/alphaconvert-List.hs)
Case Study 2: Kmeans
grep FIXME/TODO (!)
move HINT to ABOVE code block
Subtyping exercises
LH fix
convert measure refinements into invariants, e.g.
measure size :: [Int] -> Nat
should yield invariant {v:[a] | 0 <= size v}
? Intelligible parse errors
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.
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.
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?
The latex \begin{comment}
blocks are not being hidden in the html on the site.
See: https://ucsd-progsys.github.io/liquidhaskell-tutorial/03-basic.html#/subtyping
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.
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!
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.
Why is the resolver (lts-10.3) so far behind?
Is that necessary for correct functionality?
The long awaited super-pedantic review of the new chapter 2 is complete. The redlines can be found at:
https://drive.google.com/file/d/0Bx6BpheCXcBWNjh2R1Q2eDRWU1E/view?usp=sharing
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
My review of chapter 5 is complete. The redlined pdf can be found here:
https://drive.google.com/file/d/0Bx6BpheCXcBWUVZEMzdVWm9xWDg/view?usp=sharing
Overall, this chapter went a lot smoother than chapter 4. I think the pacing of exercises was good in this one. They slowly ramped up to the "big one", then there was a "real world example" that showed off some subtle problems.
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
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.
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.
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").
\tsng
, \tcap
, \tcup
are undefined (in http://ucsd-progsys.github.io/liquidhaskell-tutorial/08-measure-sets.html)
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?
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.
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
I've finished my review of chapter two. The annotated .pdf can be found at:
https://drive.google.com/file/d/0Bx6BpheCXcBWbWhHWFhtS1NHQ00/view?usp=sharing
-Annotations in blue are suggestions
-Annotations in red are errors
-Annotations in green are my solutions to the exercises. I figured it might be helpful to include these so you can see the kinds of approaches that are being taken by "people" after reading the document (sample size = 1)
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?
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)
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:
es
is unbound in the new liquid signature for head
.==
only works for Eq
types, so I'm not sure how to specify e == head es
.Feedback/help is welcome.
"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?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.