
Things that Idris improves things over Haskell - deque-blog
https://deque.blog/2017/06/14/10-things-idris-improved-over-haskell/
======
mej10
Idris points the way to the future of programming.

A few examples that I find very intriguing:

1\. type safe printf:
[https://github.com/mukeshtiwari/Idris/blob/master/Printf.idr](https://github.com/mukeshtiwari/Idris/blob/master/Printf.idr)

2\. compile-time evidence that a runtime check will be performed:
[https://github.com/idris-lang/Idris-
dev/blob/master/libs/bas...](https://github.com/idris-lang/Idris-
dev/blob/master/libs/base/Data/So.idr)

3\. Most of all, compile-time checking of state machine properties
[http://docs.idris-lang.org/en/latest/st/introduction.html](http://docs.idris-
lang.org/en/latest/st/introduction.html)

Think about being able to specify protocols in the type system and ensuring
that your client and server meet the specifications. The example in that link
is about user authentication. Imagine having a proof that the program can only
get into a "LoggedIn" state by going through the authentication protocol.

~~~
didibus
I'm sure there's a branch of programming where that would be amazing, but in
my experience, I've never had major bugs due to my client and server
implementing their protocol wrong.

In fact, I question most of the value of formal verifications for non critical
software. Most of the problematic bugs that make it through in my experience
are either a complex combination of multuple parts of the system interacting
together in a way that doesn't end up behaving the way it was intended, or
programmers implementing the wrong thing on edge cases.

~~~
haskellandchill
I have 8 years of industry experience and feel the exact opposite. When
programming in Ruby I am forced to not fully express the concepts in my head.
We do TDD but our tests are humorously thin; typically covering none, one,
many. Idris proofs are tricky but more automated and powerful than writing
tests. And I can still write tests in Idris.

To go beyond "protocols", UI form data to server response back to UI display
in a web app is a source of major bugs. So many things are broken. Recently I
had to add a spurious lap infant to book a flight due to JavaScript template
being broken when rendering 0 children.

~~~
codygman
spurious lap infant?

~~~
haskellandchill
True story:
[https://i.redd.it/lc84fv1lrv2z.png](https://i.redd.it/lc84fv1lrv2z.png)

------
unwind
Admins; please consider editing the title, it has a redundant "things" that
makes it hard to read and confusing.

The blog post's real title ("10 things Idris improved over Haskell") is
better; unless that's a problem due to being a list (which are often spammy).
Seems fine/serious to me, though.

------
zimbatm
I am not a fan of `cast` (also seen in other languages as well) as it leads to
reductionist thinking.

There are usually more than one way to convert from one type to another. How
is Float to Int rounded? Shouldn't String to Int return conversion errors?
Just looking at `cast` means I have to learn what the language decided to
default to.

~~~
vosper
String to int returning 0 for an uncastable string seems like a terrible
design. It should blow up. How otherwise can you tell between "0" and "ABC"?

~~~
SEMW
I haven't yet tried Idris (or even Haskell), so I could be misunderstanding,
but surely in a pure language, a function with a type of String -> Int can't
'blow up', it can only output an Int? So if you want to tell the difference,
you'd use something that outputs a (Maybe Int) rather than an Int, which'd be
a wrapper around `cast` that does validation you want

Looking at the interfaces tutorial[0], it gives an example of something
similar:

    
    
        readNumber : IO (Maybe Nat)
        readNumber = do
          input <- getLine
          if all isDigit (unpack input)
             then pure (Just (cast input))
             else pure Nothing
    

[0] [http://docs.idris-
lang.org/en/latest/tutorial/interfaces.htm...](http://docs.idris-
lang.org/en/latest/tutorial/interfaces.html)

~~~
empath75
If it could fail, wouldn't you want it to return an 'either' type, rather than
an int?

~~~
lodi
Yes, exactly. Here's the type signature for `cast`:

    
    
      cast : Cast from to => from -> to
    

So if the types `from` and `to` are part of the `Cast` typeclass, then cast
will always convert one into the other, without "failing". The standard
library defines a handful of "sensible" casts like Int to Float, but you can't
even attempt to cast a function to a String, or a List to an Int, since those
aren't part of the `Cast` typeclass.

For an operation that can fail, like a parse method, a more appropriate return
type is `Maybe Int`, or `Either String Int` (where the `String` is an error
message).

Also Idris provides a dependently-typed way to connect the check (`all isDigit
(unpack input)`) to the conversion itself (`cast input`), so that you can't
write an invalid check or forget to include the check at all. See my reply to
vosper above.

------
pvdebbe
I find it unfortunate how many languages treat strings as lists of chars for
no clear benefit. This issue is apparent in dynamically typed languages where
a function might expect either a string or a sequence, and now it all has to
come down to type comparisons because both can be iterated. I think it'd be
time to start treating them as atomic values instead.

~~~
adrianN
But iterating over the characters is a very useful feature. It's used in all
string algorithms that I can remember in the time it takes to write this
comment.

~~~
johncolanduoni
And those string algorithms likely break in subtle ways when they handle
characters that span multiple codepoints.

~~~
adrianN
Not if the "iterating over character" function iterates over actual characters
and not codepoints.

~~~
johncolanduoni
You mean grapheme clusters? Swift is the only language I know that uses that
by default, and you still wouldn't want to store strings as a list of grapheme
clusters.

~~~
cannam
I believe Perl 6 does so as well, see e.g.
[https://perl6advent.wordpress.com/2015/12/07/day-7-unicode-p...](https://perl6advent.wordpress.com/2015/12/07/day-7-unicode-
perl-6-and-you/)

------
nv-vn
Idris is a really great language and I recommend that anyone struggling with
Haskell give it a try. As an OCaml programmer struggling to adjust to
Haskellisms, I (ironically) ended up learning Idris before Haskell. Some of
the features -- strict evaluation by default, IO evaluation vs. execution,
more alternatives to do notation, better records, effects instead of monad
transformers -- make Idris vastly easier to understand as a beginner despite
the use of dependent types. As a language, Idris is still lacking a couple of
things I'd like (and there's still plenty of bugs), but it definitely feels
like it's a much refined version of Haskell.

------
harveywi
Frustrated Scala users take note: Idris can compile to JVM bytecode
([https://github.com/mmhelloworld/idris-
jvm](https://github.com/mmhelloworld/idris-jvm)), JavaScript (displacing
Scala.js), and native code (displacing Scala Native). Idris may be a very good
choice for a post-Scala language.

One thing that I find strange, though, is that some of the most prolific Scala
developers who are critical of the language seem to stick to languages such as
Haskell/Eta and PureScript. Maybe it's the immaturity of the Idris ecosystem.

~~~
virtualwhys
What about non-frustrated Scala users, should they drop Scala and Scala.js in
favor of an effectively experimental language with zero industry adoption?

Post-Scala is already under way with the new compiler, Dotty[1], which will
replace present day Scala.

[1] [https://github.com/lampepfl/dotty](https://github.com/lampepfl/dotty)

~~~
paulddraper
Non-frustrated Scala user here. I recommend you not switch in that case.

You'll have to admit though: the greatest strength and weakness of Scala is
Java.

------
danidiaz
An interview with the creator of Idris in the Code Podcast:
[https://soundcloud.com/podcastcode/edwin-brady-on-
dependent-...](https://soundcloud.com/podcastcode/edwin-brady-on-dependent-
types-and-idris)

Another cool feature of Idris is elaborator reflection
[https://www.youtube.com/watch?v=pqFgYCdiYz4](https://www.youtube.com/watch?v=pqFgYCdiYz4)
which I believe has no direct Haskell analogue (template Haskell perhaps?)

~~~
tcopeland
More generally if you like functional programming, the archives of the
"Functional Geekery" podcast are a great resource:

[https://www.functionalgeekery.com/](https://www.functionalgeekery.com/)

There's at least one episode that's devoted to Idris:

[https://www.functionalgeekery.com/episode-54-edwin-
brady/](https://www.functionalgeekery.com/episode-54-edwin-brady/)

------
MichaelBurge
Many of these you can work around with language extensions or a custom
Prelude, but then you need to have been bitten by them to know that you need
to work around them.

I hear "dependent types" and I think "theorem prover", but it seems like Idris
is a cleaned up Haskell with some light proving features built in?

Haskell is good for compilers and parsers. What's a good excuse to try Idris?

~~~
neel_k
> I hear "dependent types" and I think "theorem prover", but it seems like
> Idris is a cleaned up Haskell with some light proving features built in?

It's a full-strength dependent type theory, but is intended primarily for use
as a programming language. So the design focus is on using full dependent
types to make ordinary programming easier.

> What's a good excuse to try Idris?

The best excuse of all: it is super fun.

------
rnhmjoj
The problem with working with strings in Haskell is that there are too many
datatypes: Data.Text, Data.Text.Lazy, Data.ByteString Data.ByteString.Char8,
Data.ByteString.Lazy, Data.ByteString.Lazy.Char8. All of them share the same
function names so you have to do imports like

    
    
        import qualified Data.ByteString as BS
        import qualified Data.Text.Lazy as TL
        import qualified Data.Text as TS
    

and somehow the library you need always use a different ByteString variant
from the one you already chose so you have to pack/unpack here and there.
There should be a way to make `length`, `map` and all work on every string
type. Maybe a type class or some better idea is needed.

By the way the link to the caesar cipher is broken.

~~~
runeks
I'm not familiar with UTF-8, which -- I believe -- is what Data.Text is used
to represent. Do all UTF-8 strings have a well-defined length?

A list of ASCI chars obviously does (the length of the list), but I'm not sure
about Data.Text.Text.

~~~
rnhmjoj
Yes, I'm sure the length of a UTF-8 string is something tricky but Data.Text
has a length function nonetheless. The APIs are very similar if not identical.
You can see it here:

[https://hackage.haskell.org/package/text-1.2.2.2/docs/Data-T...](https://hackage.haskell.org/package/text-1.2.2.2/docs/Data-
Text.html)

[https://hackage.haskell.org/package/bytestring-0.10.8.1/docs...](https://hackage.haskell.org/package/bytestring-0.10.8.1/docs/Data-
ByteString.html)

~~~
HelloNurse
Of course, a real string type would store length along with the characters,
making the length function trivial and fast.

~~~
leshow
It's hard to say what length should be because it could mean different things.
How many u8 values there are? How many graphemes are present?

If it's just counting how many u8's in a slice, that's trivial. But storing
the total number of variable-length graphemes isn't. I assume that's why
Data.Text's length function is O(n), and other languages that have UTF-16 or
UTF-8 strings also have length functions that are linear.

~~~
HelloNurse
Graphemes? Strings can be expected to be transformed so that they contain the
"right" type of characters (in particular, plain letters followed by the
respective combining diacritical marks vs a smaller number of precomposed
accented letters) before asking about their length, which is how many
characters they contain. If you want to count "graphemes" you can have a
different function and possibly a separate cached result.

This in theory. It is readily apparent from the Data.Text page at
[https://hackage.haskell.org/package/text-1.2.2.2/docs/Data-T...](https://hackage.haskell.org/package/text-1.2.2.2/docs/Data-
Text.html) that the authors care about performance only selectively ("fusion"
of buffer allocations is fun, relatively messy data structures to cache
important data such as string length are not fun), and that they don't care
enough about Unicode to add serious string-level abstractions over the Unicode
tables exposed in the Char type.

------
insulanian
How does Idris compare to Agda (1) and F* (2)?

[1] [http://wiki.portal.chalmers.se/agda](http://wiki.portal.chalmers.se/agda)

[2] [https://fstar-lang.org/](https://fstar-lang.org/)

~~~
nv-vn
Agda is primarily a theorem prover, Idris doesn't try to compete with that.
They're rather similar in a lot of ways, but Idris targets more general
purpose programming. F* is more similar in its goals, but it kind of has its
own paradigm. I think a lot of the focus behind F* was based on crypto
applications, so it's a bit less general purpose. It also approaches I/O
without monads (to my understanding, it just uses Tot, ML, etc. to mark
effects for types). The syntax is more ML-like (not super important), but in
terms of the type system it's very strange compared to Idris (it focuses on
refinement types versus using things like GADTs to construct a lot of the
primitives -- the F* way of creating nat is to assert that an integer is >= 0
instead of defining the natural number as Zero | Succ Nat).

~~~
weavie
Indeed. Idris aims to be "PacMan complete". As in it should be perfectly
feasible to write PacMan using Idris.

------
logophobia
So, the unpack/pack solution seems a bit weird to me. Why do you need to
convert a string to a list just to iterate? I'm assuming "List" is a linked
list.

Why not have an Iterable/Enumerable typeclass/trait/interface which is
implemented by each dataset that is listly? Seems a lot more efficient and
easier to understand then having to convert between representations just to
iterate or change elements.

------
hota_mazi
Wow, I didn't realize that record field names have to be unique in Haskell.
The following:

    
    
        data Human = Human {name :: String}
        data Dog = Dog {name :: String}
    

is illegal, because they can't both have a field accessor called `name`.

That's... crazy.

~~~
mmalone
Yea... in theory it makes sense. In Haskell, field names are functions. If you
have an instance of Human and you want it's name you do 'name human' not
'human.name()'. Polymorphism is done through type classes, not by ad-hoc
overloading. So if you want the name function to take a Human or a Dog you
need to define a type class with name and have both implement it (kind of like
interfaces, but not really).

Pragmatically, it's really annoying. There's a solution in the space between
pure (a la Haskell) and magical (a la Scala) that makes sense. I think Idris
might have found it.

~~~
mrkgnao
_AFAIK_ -XDuplicateRecordFields and -XOverloadedRecordFields solve this now,
creating a typeclass for each of the record labels. In this case, the compiler
would generate a HasName class. (This is conceptually similar to classy
lenses, but minus the TH.)

------
coldtea
Wait, Idris sounds like a much improved Haskell.

Any downsides (in the core language) besides the smaller community?

Any chances for Haskell to get some of the same things?

~~~
setra
The core is based on a different type theory. It is very unlikely that the
part of GHC called "Haskell core" will make such a massive change.

In general the Idris type inference is not nearly as good as Haskells. I don't
mean this in the "dependent type inference is undecidable" sense, but instead
just generally.

Idris is also strict instead of lazy like Haskell. This is good or bad
depending on who you ask. Very unlikely for it to change in Haskell though.

Many of the other issues are the issues with every small language. Community
size and libraries.

~~~
vosper
> In general the Idris type inference is not nearly as good as Haskells. I
> don't mean this in the "dependent type inference is undecidable" sense, but
> instead just generally.

Do you know if this is just because Idris is a much less mature language than
Haskell, or its it something fundamental about the design?

~~~
Buttons840
Probably both. Dependant type systems are still an area of active research.

------
penpapersw
Idris style/semantics question: why does the last line of the first example in
the article use parentheses[1] instead of another dollar sign[2]?

[1]

    
    
        caesar_cipher : Int -> String -> String
        caesar_cipher shift input =
          let cipher = chr . (+ shift) . ord
          in pack $ map cipher (unpack input)
    

[2]

    
    
        caesar_cipher : Int -> String -> String
        caesar_cipher shift input =
          let cipher = chr . (+ shift) . ord
          in pack $ map cipher $ unpack input
    

To me the second one seems more readable. Are there semantic differences?
Performance differences?

------
alkonaut
I know idris (apart from improving some haskell warts) also adds dependent
types. Are there any good simple examples of dependent type use, that is _not_
vectors-of-length-N?

~~~
nv-vn
Yes. They're amazing for theorem proving (see Agda, Idris). They're also
useful for refinement types, proving type class laws like for monads, etc.

~~~
alkonaut
Is there a hello world-y example of refinement types, similar to Vectors with
length?

------
iso-8859-1
Interfaces are as powerful as modules in ML:

'Interfaces in Idris are similar to Haskell type classes, but with support for
named overlapping instances. This means that we can give multiple different
interpretations to an interface in different implementations, and in this
sense they are similar in power to ML modules (Dreyer 2005). Interfaces in
Idris are 1st class, meaning that implementations can be calculated by a
function, and thus provide the same power as 1st-class modules in 1ML
(Rossberg 2015).'

Quoting from [https://www.idris-lang.org/drafts/sms.pdf](https://www.idris-
lang.org/drafts/sms.pdf)

------
mijoharas
Can anyone explain the last point to me? How exactly is the example considered
"abuse"? What would an attempt to write similar in Haskell look like?

~~~
icen
The 'abusiveness' is the use of `>>=` as a _constructor_ , instead of its more
standard role as member of an interface.

The author is using it to take advantage of syntactic sugar that is now
reliant only on names and types, instead of implementation of the relevant
interface.

A similar idea is to use `::` and `Nil` as constructors, which let you use the
`[a, b, c]` list syntax (desugared to `a :: b :: c :: Nil`) for things that
aren't actually lists. It can be convenient, but can also result in confusion.

------
gjem97
Presumably one of the applications of a compiler with a built-in theorem
prover is mission critical code. But my understanding is that most mission
critical code environments prohibit recursion. What's the main target usage
here?

~~~
mmalone
I've never worked on something "mission critical," but most common uses of
recursion are tractable, so I'm not sure why it would be disallowed.

The simplest form of tractable recursion is where some argument in the
recursive call is, by some metric, "smaller" than the outer call, and is
heading towards a base case that terminates recursion when the argument
reaches "zero." Two examples are: decrementing integers towards zero, and
recursing on the tail of a list. You can prove termination and many other
properties of such a function by induction. This concept is fairly easy to
generalize to any algebraic data type, and to many other domains.

Edit: I realized I didn't really answer your question. The folks who are
working on dependently typed languages (like Idris) see them as broadly
useful, and they have a good point. Consider Java generics: they let you have
types that depend on other types, like a "List of Integers." The compiler can
make guarantees based on that type information and a lot of people consider
that useful. With dependent types you can have types that depend on _values_,
like "List of exactly 5 Integers that are all between 0 and 100." Again, the
compiler can make guarantees based on that type information, and it could
improve code safety, modularity, etc.

The challenge with dependently typed languages has been coming up with a
"surface syntax" that's easy to use. Idris (and Agda) are pretty much state of
the art there.

~~~
regularfry
"most common uses are tractable" does not guarantee that many common uses will
have been correctly analysed.

As to why it's disallowed: stack space is limited, and tail call optimisation
isn't taken for granted. Here's what the Joint Strike Fighter coding standards
(thataway -> [http://www.stroustrup.com/JSF-AV-
rules.pdf](http://www.stroustrup.com/JSF-AV-rules.pdf)) say:

    
    
        AV Rule 119 (MISRA Rule 70)
        Functions shall not call themselves, either directly or indirectly 
        (i.e. recursion shall not be allowed).
    
        Rationale: Since stack space is not unlimited, stack overflows are possible.
    
        Exception: Recursion will be permitted under the following circumstances:
    
        1. development of SEAL 3 or general purpose software, or
    
        2. it can be proven that adequate resources exist to support the maximum level of
        recursion possible.
    

So yes, you're allowed it _if you do that extra work_ , but given that you can
replace tractable recursion with a loop anyway, the win you'd have to get from
expressing the problem recursively has to compensate.

~~~
mmalone
Interesting. Thanks for the details.

In theory a compiler can do this optimization for you and can use simple
syntactic checks to determine whether recursion terminates and whether the
optimization applies (at least given a language designed with these things in
mind, I'm sure this is much harder or maybe impossible in C++). With something
more modern than C++ I'd imagine you could adopt a rule that says "you can use
recursion but we compile with --dont-allow-nonterminating-or-unoptimizable-
recursion".

------
Kiro
What is the problem with strings being lists in Haskell?

~~~
mmalone
There's a problem with strings being lists in general because it forces a
representation that's not perfect for every use case. Keeping strings abstract
and giving them their own interface (even if it's very similar to list's) lets
you optimize the representation, or even specialize the representation for
different use cases. You can intern, use vectors, use ropes, use tries, or any
number of other crazy things. It also lets you expand the string interface
beyond the list interface.

Practically, certain operations end up being slow when you use the standard
"strings are lists" mechanism provided by Haskell's standard library. Stuff
like building a list is hard or slow for stupid reasons: Haskell uses cons
lists with fast append-to-head and slow concatenation, so the common case of
building a list by appending characters to the end is slow. Or you have to
prepend and reverse. Stuff like that. It's dumb, but it matters.

The Haskell community has reacted by creating other ways to represent strings,
like `Data.Text` and `ByteString`. These representations have certain
benefits, so they're widely used. This adds _another_ problem: different
libraries use different representations, so you end up having to convert back
and forth between them all the time. Again, this is annoying and inefficient.

So yea. I think the lesson for language designers is clear. Strings are a
distinct concept. Yes, they do list-like things, but they're not lists.

------
alphonse23
Was this written in a hurry? There's lot of spelling/grammar errors in the
article.

------
hbex5
Idris is one of those languages that when I learned it felt like it was
offering me a glimpse into a possible future.

I love it when that happens.

~~~
runeks
Would you mind sharing what it was about Idris that made you feel like you
were offered a glimpse into a possible future?

~~~
tybit
I can't speak for op but I had a similar feeling going through the free
chapters of 'Type driven development with Idris'. What blew me away was once
you have such a strong type system it eliminates whole classes or errors at
compile time I previously considered only checkable at runtime. Furthermore it
showed that doing so allows the compiler to go from being a gatekeeper to
being more of a virtual assistant that helps you write your code not just
check it.

~~~
runeks
> What blew me away was once you have such a strong type system it eliminates
> whole classes or errors at compile time I previously considered only
> checkable at runtime.

Can Idris turn Haskell runtime errors into compile-time errors and, if so,
which ones?

~~~
bbatha
The classic one is the `head` function in idris can only be called on `Vec`s
of length 1 or more. More pressingly, you can actually prove lawfulness of
type classes.

~~~
aaron-santos
I'm curious how does this compare to something like Cats' NonEmptyList[1] in
Scala?

[1] -
[https://github.com/typelevel/cats/blob/master/core/src/main/...](https://github.com/typelevel/cats/blob/master/core/src/main/scala/cats/data/NonEmptyList.scala)

