Making the most out of DAML Compiler warnings

If you’ve used DAML Studio or the command line compiler, you will have noticed that it warns you about potential issues in your code.

Screenshot from 2020-06-26 11-53-52
In the example above, I get a warning for a redundant import of Daml.Script and I also get a warning about a pattern match that is not-exhaustive (I didn’t match on Right).

These are not fatal errors. The compiler can still produce a DAR and you can test out how your UI works against this model. However, they are usually issues worth investigating before you merge your changes since they either indicate bad practices (redundant imports) or point to actual errors (non-exhaustive pattern matches).

Turning warnings into errors

While it is convenient to test out models that still have warnings, you probably don’t want to commit them. To enforce that you can make the compiler fail if there are any warnings. You can then use this in CI so any builds with warnings will fail and no warnings accidentally make it through to the code that you deploy to production. All you need to change here is to switch from daml build to daml build --ghc-option=-Werror and all warnings will instead result in fatal errors!

Enabling additional warnings

The default set of warnings enabled by the DAML compiler is fairly conservative. Especially, when you are just starting out seeing a huge set of warnings can easily be overwhelming and distract from what you are actually working on. However, as you get more experienced and more people work on the same projects enabling additional warnings (and enforcing their absence in CI) can be useful.

All of these warnings can be enabled by adding --ghc-option=-W<warningname> to the list of build-options in your daml.yaml. You can disable warnings via --ghc-option=-Wno-<warningname>.

unused-top-binds

This warning tells you if you have unused top-level definitions, i.e., dead code that is not used anywhere and can be deleted. Note that this only works if you specify the list of exported definitions explicitly. Otherwise all top-level definitions are exported implicitly so they could be used from another module and therefore cannot necessarily be deleted. To specify them explicitly switch from
module A where
to
module A (export1, export2) where.

unused-matches

Note: This requires SDK 1.3.0-snapshot.20200623.4546.0.4f68cfc4 or newer to work properly. For older versions, templates produce false positives. Special thanks to @shaynefletcher for fixing this.
-Wunused-matches tells you about patttern matches where you bound a variable but didn’t use it. This can indicate that you passed an unused parameter to a function, e.g.,

f x = 0

Here you will get a warning about x being unused. If you do this intentionally you can replace the x by an underscore or any variable name starting with an underscore, e.g.,

f _x = 0

unused-do-bind

This is similar to unused-matches but specific to do-blocks.

f p = do
  cid <- submit p (createCmd …)
  pure ()

Here you will get a warning that cid is unused. As with unused-matches you can silence it by prefixing the variable with an underscore.

incomplete-uni-patterns

By default, the compiler warns you about non-exhaustive pattern matches. But it doesn’t warn you in places where you can only specify one pattern, e.g., a lambda or a let-binding. incomplete-uni-patterns also warns about those cases.

f = (\(Left x) -> x)

redundant-constraints

As the name suggests, this warning warns you about redundant constraints on a definition, e.g.,

f : Eq a => a -> a
f x = x

There is no reason to add the Eq a constraint here since we are not using == or /=. The extra constraint makes our function less useful and also incurs a runtime overhead.

missing-signatures

While the DAML compiler is often able to infer the type of an expression, it is good practice to annotate top-level definitions with an explicit type signature to make your code easier to read, get better compile errors on type mismatches and potentially avoid runtime overhead from making your function more general than you intended to. -Wmissing-signatures warns you about any top-level definition without a type signature. Note that you need to write the type signature separately to silence the warning.

f (x: Int) : Int = x + 1

will still produce the warning while

f : Int -> Int
f x = x + 1

will not.

Enabling all common warnings

Enabling all warnings individually can get tedious. Instead you can add --ghc-option=-Wall to your daml.yaml enable all of them and then selectively disable warnings via --ghc-option=-Wno-<warningname> that you don’t want to use on your project

What are your favorite warnings?

Did i miss any that you consider helpful? Which one helps you the most in writing better code with less errors?

14 Likes

Very useful post @cocreature! incomplete-uni-patterns seems particularly important - not sure why GHC doesn’t enable it by default.

Great post @cocreature

The only reason I can think of why you might not enable incomplete-uni-patterns is because it’s really annoying when experimenting in GHCi; you can’t just define one-liners when you know how things will resolve.

If the defaults are the same for both GHC and GHCi (to make life simpler for the user, or for technical reasons), I can imagine why you might want to err towards newbie-friendliness in the REPL, rather than correctness.

Where can I get a list of warning names?

The warnings all come from GHC (the Haskell compiler which we based the DAML Compiler on) so you can take a look at https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-warnings.html. Note that not all warnings might apply to DAML and some of them might lead to false positives.

1 Like

thanks for these handy tips,

Id find it useful to have a concrete example of some recommended flags for people to copy and paste into their daml.yaml, as its a little tricky to setup, perhaps in this area of docs as i had to refer here to get the syntax right. https://docs.daml.com/tools/assistant.html#project-config-file-daml-yaml

Here is what I have tried out after reading this post, key observations are

  • “-Wno-unused-matches”, otherwise daml studio lights like a christmas tree and displays warnings of unused this, self args in templates :stuck_out_tongue:

  • I think its unused-do-bind, not with an ‘s’ on the end as daml build said it couldnt recognise it as a valid flag.

build-options: [

  "--ghc-option", "-Wall",

  "--ghc-option", "-Wunused-do-bind",

  "--ghc-option", "-Wincomplete-uni-patterns",

  "--ghc-option", "-Wredundant-constraints",

  "--ghc-option", "-Wmissing-signatures",

  "--ghc-option", "-Wno-unused-matches",
  ]

is there any way to get rid of shadowing warnings for daml related stuff but still have it enabled for everything else?

  This binding for ‘key’ shadows the existing binding
  imported from ‘Prelude’ at daml ...
  (and originally defined in ‘DA.Internal.Template.Functions’)
3 Likes

The -Wunused-matches warning only works in recent snapshots as I mentioned. In older snapshots you get the false positives you encountered.

As for shadowing, at this point you cannot turn this off for builtin functions.

1 Like

Seconding matt’s request for recommended flags in the docs

1 Like

I’ve added a section with a copy-pastable version of these warnings in this PR, which you can see live in the latest documentation. Thanks for the suggestion.

1 Like