TomP
TomP

Reputation: 108

Is there a naming convention for constants in Haskell

I am writing a simulator for a spiking neural network. There are tons of constants, time constants, voltage levels, etc.

Sometimes when I'm looking at my code and see a constant name, I look around the function to see where it's defined not realizing that it's one of the constants (until a few seconds later) .. I love Haskell and programming in it is super comfortable except for this. In Scheme, I think there was a convention of starting constant names with an "*".

I've looked at several coding convention sites and couldn't find anything.

Is there a Haskell convention for this or should I just invent my own, such as the first letter being "k" .. kTauOne, kSpikeRateMax ..

Thanks,

Tom

Upvotes: 3

Views: 265

Answers (3)

Jon Purdy
Jon Purdy

Reputation: 54971

If your constants have a sensible notion of equality (unlike, say, pi @Double) then you can define them using bidirectional pattern synonyms. This isn’t very common, but it’s a good middle ground between a normal constant and a data or newtype definition.

{-# Language OverloadedStrings, PatternSynonyms #-}

import Data.Text (Text)
import Data.Text qualified as Text

pattern FieldSeparator :: Char
pattern FieldSeparator = '-'

pattern LayerPrefix :: Text
pattern LayerPrefix = "layer-"

If you defined the constant fieldSeparator = '-', you might use it with a Boolean guard:

nextField (c : _) | c == fieldSeparator = Nothing

Whereas, you can use the bidirectional pattern FieldSeparator in both patterns and expressions.

nextField (FieldSeparator : _) = Nothing
Text.intercalate
  (Text.singleton FieldSeparator)
  ["turnip", "parsnip", "rutabaga"]

You can also combine these into larger bidirectional patterns.

{-# Language ViewPatterns #-}

pattern Layer :: Text -> Text
pattern Layer suffix <- (Text.stripPrefix LayerPrefix -> Just suffix)
  where
    Layer suffix = LayerPrefix <> suffix
case "layer-five" of
  Layer layer -> Right ("layer: " <> layer)
  other -> Left ("not a layer: " <> other)

Another guideline: if you feel the need to use the {-# Complete #-} pragma to tell the compiler that pattern-matching on some set of constants is exhaustive, at that point, it’s almost certainly easier to make a data type.

Upvotes: 2

leftaroundabout
leftaroundabout

Reputation: 120711

No, there is no such convention and there shouldn't be. After all, in some sense all variables are constants in Haskell (within the right scope), as are all functions, and even those constants that would actually correspond to global constants in an imperative language can serve very different purposes.

That said, you can absolutely organise your constants / variables / whatever with whatever prefix you wish, but don't hard-link it to the name. Instead, put the constants in a module and import it qualified!

module MyKoolKonstantz where

τOne :: Double
spikeRateMax :: Double
...

module TehPrograhmStufz where

import qualified MyKoolKonstantz as K

simThoseSpikes :: Foo
simThoseSpikes = quinum (K.spikeRateMax)

Often, actually using global constants isn't a good idea anyway: they may be constant right now, but perhaps soon you need two different kinds of simulations with different constants?

That's where it makes sense to put them into a configuration type and pass that an argument.

data SpikeNNConf = SpikeNNConf
  { τOne, spikeRateMax, ... :: Double }

simThoseSpikes :: SpikeNNConf -> Foo
simThoseSpikes cfg = quinum (spikeRateMax cfg)

Here the cfg also kind of acts as a marker for "that's a constant".

Upvotes: 6

Daniel Wagner
Daniel Wagner

Reputation: 152707

I'm trying to think of constants (=values exported from a library with non-function types) and coming up pretty shy on examples. pi is about the only really pure constant that comes to mind. There's zero-field constructors all over the place (False, Nothing, LT, (), []), recognizable by their leading capital letter with just a few exceptions; and there's algebraically-relevant constants in some classes (mempty, zero, empty, one) and common APIs (empty from various container libraries). Arguably the base-case pretty-printers from various pretty-printer libraries qualify, e.g. emptyDoc, line, softline, hardline, colon, lparen, space from prettyprinter.

Among these examples, there doesn't seem to be a particularly strong convention. I say make up a convention that seems sensible to you and run with it.

Upvotes: 2

Related Questions