L
LiquidHaskell Blog
ucsd-progsys.github.io · Company blog · on HN since 2016
The UCSD programming systems group writes about LiquidHaskell, refinement types and verifying Haskell programs.
What HN saysHN commenters point to it as a good example of refinement types, with an interactive tutorial they find amazing for proving properties about code.
42HN people shared it
38linked it in comments
8stories submitted
27story points
9 months agolast shared
2016people per year2026
Most-shared posts
- 1LiquidHaskell19 people · 3 points · first shared 2017 · discussion on HN
- 2Liquidhaskell9 people · first shared 2022
- 3Liquid Types vs. Floyd-Hoare Logic3 people · 8 points · first shared 2019 · discussion on HN
- 4Dependent Types now almost batteries-included in GHC Haskell2 people · 10 points · first shared 2020 · 1 comments on HN
- 5Polymorphic Perplexion2 people · 5 points · first shared 2020 · discussion on HN
- 602 logic2 people · first shared 2016
- 701 intro2 people · first shared 2019
- 8Programming with Refinement Types: An Introduction to LiquidHaskell1 people · 1 points · first shared 2019 · discussion on HN
- 901 index1 people · first shared 2018
- 1003 basic1 people · first shared 2019
- 11Tutorial 05 Datatypes1 people · first shared 2023
- 12Refinement reflection.lhs1 people · first shared 2020
- 132019 10 20 why types.lhs1 people · first shared 2022
- 14Tutorial 01 Introduction1 people · first shared 2021
- 1505 cobra1 people · first shared 2019
What HN people wrote when they linked it
eddsolves · 2024-12-25
I think liquid Haskell is a great example of refinement types?
https://ucsd-progsys.github.io/liquidhaskell/
jpolitz · 2019-09-13
Various flavors of a course based on "An Incremental Approach to Compiler Construction" [1] have most or all materials free online, some with excellent notes. Taught at UCSD, Northeastern, Swarthmore College:
https://ucsd-cse131-s18.github.io
https://course.ccs.neu.edu/cs4410/lec_let-and-stack_notes.html
https://ucsd-progsys.github.io/131-web/lectures/05-cobra.html
(I designed the original version, though it's improved a lot in the past few years)
[1] http://scheme2006.cs.uchicago.edu/11-ghuloum.pdf
garethrowlands · 2019-04-16
Often it's best to just express preconditions in the normal types. For example, instead of having a `head-of-list` function that crashes with empty input, define its input to be of type `NonEmptyList`.
Refinement types are also worth exploring in this space, as they appear to give a good power-to-weight. See Liquid Haskell, for example, https://ucsd-progsys.github.io/liquidhaskell-tutorial/03-basic.html
airesQ · 2016-12-07
There is an interactive tutorial at [1].
I find this quite amazing. With refinement types one can say/prove things like:
- This function only returns even numbers.
- The input for this function is not just any vector, it must be an ordered vector.
- It's possible to make compile time unit tests. Where we write a function that exercises the code, and returns True if the test succeeds. Then one adds a refinement type saying that the function must always evaluate to true. The theorem prover does the rest.
[1] - http://ucsd-progsys.github.io/liquidhaskell-tutorial/02-logic.html#/ex…
tikhonj · 2018-08-10
This kind of type becomes really tedious to use unless there is some sort of viable "subtyping"—you certainly want to be able to use a non-zero number where any number works and, ideally, you want to be able to recover a non-zero number when you write a function that takes in a normal number and always returns a positive value. If you're forced to use a bunch of explicit conversion functions to achieve this you add a lot of noise to your code and, generally, the most common function is going to be along the lines of "I know this is positive, so throw an error if it's 0"—which is exactly what we get anyway.
Explicit might be better than implicit most of the times, but it is possible to be too explicit.
That said, we can achieve this, although it's probably a bit trickier than you'd think at first. The best option I know is called "refinement typing", which basically allows you to specify types as subsets (ie "the set of integers which are not 0") and automatically verifies whether your functions actually return a value in the subset.
Liquid Haskell[1] is an example of a system you can use today to play with the idea. It uses an automatic logic solver (Z3) under the hood to automatically prove that your refinements hold. (For example, I believe it should be able to automatically verify that the function f x = abs x + 1 will never be 0.)
[1]: https://ucsd-progsys.github.io/liquidhaskell-blog/
Tehnix · 2017-12-05
From what I gather, this happens at runtime, which makes me feel it's a bit unfortunate to call it dependent types, or at least my view of dependent types is of it being a compile-time guarantee.
Take for example Liquid Haskell[0] where you can, at compile time, make guarantees about your code. For example, here's a function that makes division safe, by never allowing the second value, y, to be 0,
{-@ safeDiv :: Int -> {v:Int | v != 0} -> Int @-}
safeDiv :: Int -> Int -> Int
safeDiv x y = x `div` y
The second annotation is plain Haskell meaning it takes 2 `Int`s and returns an `Int`, with the third line being the function body. The first {-@ .. -} annotation is the one Liquid Haskell reads, stating it takes an `Int` as the first argument, another `Int`, which we now refer to as v, that is not 0, and finally returns an `Int`.
Another practical language with dependent types is Idris[1], which supports full dependent types with dependent pattern matching.
Finally, for the "why would I use this", I recommend checking out Gabriel Gonsalez's keynote, from Haskell eXchange 2017, "Scrap your Bounds Checks with Liquid Haskell"[2]
[0] https://ucsd-progsys.github.io/liquidhaskell-blog/
[1] https://www.idris-lang.org
[2] https://skillsmatter.com/skillscasts/10690-keynote-scrap-your-bounds-c…
dwohnitmok · 2017-12-05
If you're talking about the restricted/smart constructor approach that I think is exemplified by this library, i.e. the approach that hides the construction of a type behind a mandatory verification function, it's a pattern that's been around for a long time. OOP constructors are perhaps the best known example, although they often through an exception on invalid input rather than represent this at the type level with an Option/Maybe type or Either type.
I personally think that this approach (with the type level tracking) is indeed woefully underused (and is an easy-to-understand-and-implement win in most codebases) and would usually chalk it up to lack of familiarity in the community.
That being said, there are definitely domains that can make this approach (without the help of general dependent types) cumbersome to the point that I would consider scrapping it. One example of this is doing statistics and collection types. Most statistical operations operate over a collection of values. This is easily supplied by a standard collection type such as List or Vector. A significant chunk require a non-empty collection of values (think aggregate statistics such as mean). A type such as NonEmptyList or NonEmptyVector comes in handy here along with the corresponding verification function. A smaller chunk require collections of at least two elements (e.g. a sample estimate of standard deviation). Now you need a separate AtLeast2ElemList with a separate verification function.…
Iceland_jack · 2024-03-08
With https://ucsd-progsys.github.io/liquidhaskell/, but as a separate checker. I think there are talks of integrating it into the compiler
ddellacosta · 2024-03-06
Oh yeah, you just reminded me of this cool talk I saw at Strange Loop a while back. Not about the AI parts but re: program synthesis in Haskell:
"Type-Driven Program Synthesis" by Nadia Polikarpova https://www.youtube.com/watch?v=HnOix9TFy1A
Links to more projects and papers by Prof. Polikarpova: https://cseweb.ucsd.edu/~npolikarpova/
I think this is one of the main projects she discusses in the talk: https://github.com/nadia-polikarpova/synquid
EDIT: meant to mention this too, which I think has been around a bit longer, not that I've ever used it in production: https://ucsd-progsys.github.io/liquidhaskell/
antipurist · 2023-10-02
> What ensures that the “rest of the string” is, in fact, a suffix of the input?
Nothing, unless you have unit tests. Or use Liquid Haskell:
https://ucsd-progsys.github.io/liquidhaskell/
There are languages that let you express stronger guarantees/requirements via type system, but it’s better not to go down the rabbit hole of dependent typing.
nequo · 2023-01-25
LiquidHaskell[1] implements refinement types for Haskell. Would that do what you'd like?
[1] https://ucsd-progsys.github.io/liquidhaskell/
quickthrower2 · 2022-07-13
“A serious take on a contract-based language“
Not ambitious enough. We already have dependent type systems and what I will call “property based types” since I forget the proper name e.g. https://ucsd-progsys.github.io/liquidhaskell-blog/.
The compiler
can check anything from silly out of bounds conditions to more complex assertions.
Even without that, a good type system will allow you to alias types but have a constructor do a check. Once you use the Percentage0_100 type you know that it must be in that range, and the method doesn’t need to check it again.
In think Haskell is “too much” for a lot of teams, but sprinkling in some compile time assertions and making that ergonomic with linting
and so on would be a boon on par with async/await.
“Unit test?” no need, I have a proof!
Jhsto · 2022-03-08
In a way, writing types is proof-esque. It establishes some basic correctness guarantees regardless of the programming language. Some concepts like refinement types make it seem more "mathy", by requiring quantifiers within programs: https://ucsd-progsys.github.io/liquidhaskell/blogposts/2019-10-20-why-…
Someone without any background in the subject would probably find Dafny interesting.
exdsq · 2022-01-24
A really nice thing in Haskell beyond dependent types is refinement types. Not sure if they exist in Scala - I'm sure someone has hacked something together somewhere.
https://ucsd-progsys.github.io/liquidhaskell-blog/
fault1 · 2021-12-09
One problem is Julia is just ahead of time compiled, so you need something like JET to iteratively run stability analysis via abstract interpretation if you intend to run it for a top level program:
https://aviatesk.github.io/JET.jl/dev/optanalysis/
It recently got support on vscode.
What was available in Julia before (things like @code_typed) are only applicable locally, so there is only limited benefit. Because linters have dramatically improved in recent years, it feels like Julia was lagging behind until recently.
Actually thinking about it, Julia could use something like refinement types ala Liquidhaskell: http://ucsd-progsys.github.io/liquidhaskell-tutorial/Tutorial_01_Intro…
sanity31415 · 2021-07-08
Doesn't Liquid Haskell[1] use a SAT solver?
[1] https://ucsd-progsys.github.io/liquidhaskell-blog/
the-smug-one · 2021-01-31
I don't know about that. You can obviously use them for similar things. Namely, proving properties about your software.
Here's a good blog post talking about type theory vs logics: https://ucsd-progsys.github.io/liquidhaskell-blog/2019/10/20/why-types…
sankha93 · 2020-11-02
What you are looking for are refinement type systems. LiquidHaskell [0] is the most well known refinement type system out there, to specify and verify these kind of assertions.
[0]: https://ucsd-progsys.github.io/liquidhaskell-blog/
lexi-lambda · 2020-11-02
As was already mentioned in another reply, what you are describing are refinement types. A refinement type system actually exists for Haskell: it’s called LiquidHaskell,[1] and though I have not used it for anything serious, it seems to work well for certain kinds of problems.
The main challenge of refinement types is that arbitrary properties are very difficult to check in general. (If that weren’t the case, software verification would be easy!) I wrote about this at length in my previous blog post,[2] and Hillel Wayne wrote about it earlier this year from another perspective.[3]
[1]: https://ucsd-progsys.github.io/liquidhaskell-blog/
[2]: https://lexi-lambda.github.io/blog/2020/08/13/types-as-axioms-or-playi…
[3]: https://www.hillelwayne.com/post/constructive/
vrnvu · 2020-07-30
In recent years there has been a lot of improvement in refinement types which is, from what I understood, what you are describing. See for example Liquid Haskell that uses Z3 solver for static verification.
https://ucsd-progsys.github.io/liquidhaskell-blog/
I'd say that when dependent + refinement types get more mature and get pass the basic prototype idea in research languages/environments, these ideas could easily transfer to mainstream static typed languages and be a new module of future compilers.
asib · 2020-06-08
For me that's a game changer in terms of using Nim for future projects. I've used LiquidHaskell[1] a bit but always been hesitant to use in production just because I'm not confident enough with the Haskell type system. Nim's been on my radar for a while but have held off diving into it for whatever reason, so this is very cool to see.
[1]: https://ucsd-progsys.github.io/liquidhaskell-blog/
mintplant · 2020-03-15
I know/have worked with the team behind it at UCSD. They've mostly moved on to LiquidHaskell [0].
[0] https://ucsd-progsys.github.io/liquidhaskell-blog/
ghostwriter · 2020-03-10
> Is there a Haskell extension like Idris's totality checking?
There's LiquidHaskell https://ucsd-progsys.github.io/liquidhaskell-blog/
patrickmn · 2019-10-29
Readers may also be interested in LiquidHaskell:
https://ucsd-progsys.github.io/liquidhaskell-blog/
https://ucsd-progsys.github.io/liquidhaskell-tutorial/01-intro.html
The Python hypothesis package strikes me more like a dynamic version of Haskell/LiquidHaskell than Haskell/hedgehog.
T-R · 2019-09-29
SAT solvers are starting to be used as tooling for functional languages - Liquid Haskell uses one for refinement types[1], and Djinn uses one for suggesting functions given a type[2]. Similarly to Djinn, Edwin Brady gave a presentation on using an SMT solver to do live code suggestions/implementation inference in the successor to Idris[3].
[1] https://ucsd-progsys.github.io/liquidhaskell-blog/
[2] http://lambda-the-ultimate.org/node/1178
[3] https://www.youtube.com/watch?v=mOtKD7ml0NU
KirinDave · 2018-09-28
If you're like me, this immediately lead you to ask, "How are Refinement Types different from Dependent Types?"
In searching for that I found a really good CS stackexchange answer on the subject: https://cs.stackexchange.com/questions/21728/dependent-types-vs-refine…
Which linked to the course materials for a class on Liquid Haskell, which is a refinement types system for Haskell whic lets you prove properties about Haskell programs.
http://ucsd-progsys.github.io/lh-workshop/01-index.html
I found these helpful to think about how Synquid might be doing what it's doing. I really wish I had been at the Strangeloop talk.
PeCaN · 2017-12-11
Reminds me a lot of LiquidHaskell, you should take a look at what they've done if you haven't seen it yet. It's a very good idea IMO.
https://ucsd-progsys.github.io/liquidhaskell-blog/
ghkbrew · 2017-12-11
Have you looked into research regarding refinement types? Because they describe almost exactly what your reaching for (types with assciated provable assertions). Specifically Liquid Haskell[0] extends Haskell with (dependent) refinement types, but keeps the type checking decidable and the proof burden relatively light by using a restricted predicate language and an SMT solver.
[0] https://ucsd-progsys.github.io/liquidhaskell-blog/
whilenot-dev · 2026-02-07
Conflating types in binary operations hasn't been an issue for me since I started using TS in 2016. Even before that, it was just the result of domain modeling done badly, and I think software engineers got burned enough for using dynamic type systems at scale... but that's a discussion to be had 10 years ago. We all moved on from that, or at least I hope we did.
> Now we all should be looking towards fail-safe systems, formal verification and domain modeling.
We were looking forward to these things since the term distributed computing has been coined, haven't we? Building fail-safe systems has always been the goal since long-running processes were a thing.
Despite any "past riddles", the more expressive the type system the better the domain modeling experience, and I'd guess formal methods would benefit immensely from a good type system. Is there any formal language that is usable as general-purpose programming language I don't know of? I only ever see formal methods used for the verification of distributed algorithms or permission logic, on the theorem proving side of things, but I have yet to see a single application written only in something like Lean[0] or LiquidHaskell[1]...
[0]: https://lean-lang.org/
[1]: https://ucsd-progsys.github.io/liquidhaskell/
valcron1000 · 2024-09-12
This is actually a very good comment. Haskell has had these features since early ~2000s and it was a major competitive advantage in the language space, but today I would argue that if you're using a modern language then they don't stand out as much. Nevertheless, not all popular languages provide all the above mentioned features and in case they implement them it's usually in a compromised fashion. For example, nullable types are still an open issue in Java, while C# and Typescript provide easy ways to circumvent them, a lot of times by accident (the main issue is that they're mostly annotations, not runtime checks).
On the other hand, you mention several features which Haskell provides, usually through libraries that can only be implemented due to the features provided by the base language:
- Refinement types [1] allow to add runtime invariants to existing types in an ergonomic fashion, or you can go even further and use something like LiquidHaskell[2] to enforce properties at compile time.
- For multithreaded programs, the existence of STM[3] allows to to write mutable variables which are safe to use across threads. Very few languages offer something like this.
- For structural dependencies, you can apply the techniques of "ghost of departed proofs"[4]. Personally I don't like to go that route since programming becomes an act of "proving" rather than "doing" but I appreciate the fact that you can encode it if you want/need to.
- Metaprogramming in Haskell is not as…