bitemyapp / bitemyapp/esqueleto
Case analyzing enumerable types
- Dominant language
- Haskell
- Stars
- 399
- Forks
- 107
- Avg merge
- 1d 17h
- Merged PRs (30d)
- 1
Description
Hello. Does it make any sense to have a function like this in the library:
```haskell
-- |
-- Given ...
-- - an SQL expression that produces a value of some enum
-- - and a function that maps the enum to SQL expressions
-- ... produces an SQL expression that @CASE@-s on the enum value and gives the appropriate result
enumCase ::
forall e a.
(Enum e, Bounded e, PersistField e, PersistField a) =>
SqlExpr (Value e) ->
(e -> SqlExpr (Value a)) ->
SqlExpr (Value a)
enumCase choiceExpr f = case_ (caseEntry <$> [minBound ..]) (unsafeSqlValue "NULL")
where
caseEntry :: e -> (SqlExpr (Value Bool), SqlExpr (Value a))
caseEntry e = (val e ==. choiceExpr, f e)
```
This allows us to use arguably more natural Haskell case analysis to dispatch on an SQL expression representing an enumerable type, instead of constructing a syntax tree using `case_`, `when_`, `then_`, `else_` etc. For example instead of:
```haskell
case_
[ when_
(account ^. SomeEnumField ==. val SomeEnumValue)
then_
foo
, when_
(account ^. SomeEnumField ==. val SomeOtherValue)
then_
bar
]
(else_ baz)
```
we can have:
```haskell
enumCase (account ^. SomeEnumField) $ \case
SomeEnumValue -> foo
SomeOtherValue -> bar
_ -> baz
```
Note that I'm assuming the SQL type is no larger than the Haskell type that represents it: if this is sometimes not the case, it is safer to accept a default value instead of using `unsafeSqlValue "NULL"`:
```haskell
-- |
-- Given ...
-- - an SQL expression that produces a value of some enum
-- - a default SQL expression
-- - and a function that maps the enum to SQL expressions
-- ... produces an SQL expression that @CASE@-s on the enum value and gives the appropriate result
enumCase ::
forall e a.
(Enum e, Bounded e, PersistField e, PersistField a) =>
SqlExpr (Value e) ->
SqlExpr (Value e) ->
(e -> SqlExpr (Value a)) ->
SqlExpr (Value a)
enumCase choiceExpr defaultExpr f = case_ (caseEntry <$> [minBound ..]) defaultExpr
where
caseEntry :: e -> (SqlExpr (Value Bool), SqlExpr (Value a))
caseEntry e = (val e ==. choiceExpr, f e)
```
Contributor guide
No contributing guide indexed for this repository
Research direction
Review the existing case_, when_, then_, and else_ combinators alongside the two proposed enumCase signatures. Resolve whether enumCase should use an implicit NULL fallback or require a default expression, then confirm the chosen API against the library's current conventions and coverage needs.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- haskell, sql
- Domain
- databases
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100