haskell-domain-errorslisted
Install: claude install-skill ivelten/claude-haskell-skills
# Haskell Domain Errors
Errors are domain types, not strings. Each error constructor carries the structured context relevant to the failure. Rendering for humans and serialization for logs are *separate concerns* on top of that structured type. The error type *is* the data; how it surfaces (return value, effect, exception) is an orthogonal decision.
## The pattern
```haskell
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson (ToJSON(..), object, (.=))
import Data.Text (Text)
import qualified Data.Text as T
import GHC.Generics (Generic)
data RegistrationError
= EmailAlreadyExists Email
| InvalidEmailFormat Text -- ^ the raw input that failed
| PasswordTooShort Int Int -- ^ received length, minimum required
| DatabaseUnavailable Text -- ^ driver message
deriving stock (Eq, Generic)
-- Human-readable Show: ready for console, log line, or UI message.
instance Show RegistrationError where
show = \case
EmailAlreadyExists (Email e) ->
"Email already registered: " <> T.unpack e
InvalidEmailFormat raw ->
"Invalid email format: " <> show raw
PasswordTooShort got need ->
"Password too short: got " <> show got
<> " chars, need at least " <> show need
DatabaseUnavailable reason ->
"Database unavailable: " <> T.unpack reason
-- Structured JSON: tagged discriminator + payload, suitable for log aggregators