Reputation: 108
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
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
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
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