Handling multiple match cases with single statement in Elm - elm

I know in Scala you can handle multiple patterns with a single expression, is something like this possible in Elm?
l match {
case B(_) | C(_) => "B"
}

In Elm, you can only match upon one pattern at a time, unless you are pattern matching on the underscore character, which catches all.
case l of
B _ -> "B"
C _ -> "B"
...
-- or...
case l of
...
_ -> "B"
If you have something more complex that a string, it is probably best to pull it into its own function:
let
doB -> "B"
in
case l of
B _ -> doB
C _ -> doB
...

You can do something similar to the following
sample expression =
case expression of
Case1 -> "String"
Case2 -> sample Case1
Case3 -> sample Case1

Related

Doing BNF for a simplified SQL “where” clause

I have the following SQL statement with a rudimentary BNF applied:
SELECT
from_expression [, from_expression] ...
FROM
from_source
WHERE
condition
from_source:
table_name
from_expression:
literal # 'abc', 1,
column # Table.Field1, Table.Profit
function # ABS(...)
operator invocation # NOT field1, 2+3, Genres[0], Genres[1:2], Address.name
condition:
???
For now the WHERE condition is the same as the from_expression but evaluated as a boolean. What would be the proper way to show that?
The grammar doesn't care about semantics.
Syntactically, an expression is an expression, nothing more. If you later do some kind of semantic analysis, that is when you'll need to deal with the difference. You might do that in the reduction actions for condition and from_expression but it would be cleaner to just build an AST while parsing and do the semantic analysis later on the tree.
One option would be as follow, with examples in-line:
expression:
literal # 'abc', 1,
column # Table.Field1, Table.Profit
function call # ABS(...)
operator invocation # NOT field1, 2+3, Genres[0], Genres[1:2], Address.name
condition:
expression { = | != | < | <= | > | >= | IN } expression # Revenue > 0
expression IS [NOT] NULL # Revenue IS NOT NULL
condition { AND | OR } condition # Revenue > 0 AND Profit < 100
This is a comment that doesn't fit in the comments section.
I was hesitant to provide the link to the LiveSQL implementation, since it's specifically for Java language, it's open source, but we have never added any documentation whatsoever. Read at your own risk.
The main class is LiveSQL.java: in line 113 you can see the main variant of the select clause. It has many variants, but this is the one that allows the developer to include as many result set columns (expressions) as needed:
public SelectColumnsPhase<Map<String, Object>> select(
final ResultSetColumn... resultSetColumns) {
return new SelectColumnsPhase<Map<String, Object>>(this.sqlDialect,
this.sqlSession, this.liveSQLMapper, false,
resultSetColumns);
}
Of course, the SELECT clause has many other variants, that you can find in the same class if you explore it a bit. I think I was pretty exhaustive when I researched all variants. It should be [mostly] complete, except for non-standard SQL dialect variations that I didn't consider.
If you follow the QueryBuilder up the WHERE phase you can see how the predicate is assembled in the method where(final Predicate predicate) (line 101) of the SelectFrom.java class, as in:
public SelectWherePhase<R> where(final Predicate predicate) {
return new SelectWherePhase<R>(this.select, predicate);
}
As you can see the WHERE clause does not accept any type of expression. First of all, it only accepts a single expression, not a list. Second this expression must be a Predicate (boolean expression). Of course, this predicate can be as complex as you want, mixing all kinds of expressions and boolean logic. You can peek at the Predicate.java class to explore all the expressions that can be built.
Condition
Let's take as an example the Predicate class described above. If p and q are boolean expressions and a, b, c are of [mostly] any type you can express the condition as:
condition:
<predicate>
predicate:
p and q,
p or q,
not p,
( p ),
a == b,
a <> b,
a > b,
a < b,
a >= b,
a <= b,
a between b and c,
a not between b and c,
a in (b, c, ... )
a not in (b, c, ... )
Of course there are more operators but this gives you the gist of it.

Is there a way to pass the name of a field to a setter function?

Here I have several functions that all just set a single field on a model record.
In a more dynamic language, I'd just have a single setter function and pass it the name of the field (as a string) and the value that I want to set on the model object.
Is there a way to pass the name of the field in Elm?
What's the Elm way of doing something like this?
type alias Patient =
{ id : String
, name : String
, dateOfBirth : String
, sex : String
... other fields
}
setPatientName : Patient -> String -> Patient
setPatientName patient value =
{ patient | name = value }
setPatientDateOfBirth : Patient -> String -> Patient
setPatientDateOfBirth patient value =
{ patient | dateOfBirth = value }
setPatientSex : Patient -> String -> Patient
setPatientSex patient value =
{ patient | sex = value }
... many others
-- idx is the index of the patient in the model (which is an array of patients)
-- UpdateCell is a variant of my Msg type, like this: UpdateCell Int (Patient -> String -> Patient) String
onInputHandler : Int -> (Patient -> String -> Patient) -> String -> Msg
onInputHandler idx setter inputText =
UpdateCell idx setter inputText
-- idx is the index of the patient in the model (which is an array of patients)
createTableRow : Int -> Patient -> Html Msg
createTableRow idx patient =
...
, input [ type_ "text", onInput (onInputHandler idx setPatientName), value patient.name ] []
, input [ type_ "text", onInput (onInputHandler idx setPatientDateOfBirth), value patient.dateOfBirth ] []
...
I'm currently using each of these functions as an event handler for input elements. So I need a function that I can use for handling the input event. Ideally, I'd define just a single function and use that single one for all the input elements and pass it the field I want to update on the patient record.
The short answer is "no". But this seems a bit like an XY problem. It's not clear what benefit you are trying to achieve since the full application of such a function would be longer than the equivalent record update expression:
setField "name" patient value
-- vs
{ patient | name = value }
and as a partially applied function is only slightly shorter than the equivalent anonymous function with shortened argument names:
setField "name"
-- vs
\r x -> { r | name = x }
Although the latter is significantly noisier with all the symbols.
There is also a short-hand function for getting a record field:
.name
-- vs
\r -> r.name
So there is some precedent for having a dedicated syntax for setter functions too, but unfortunately there is not. Likely because it would complicate the language, and the syntax in particular, for relatively little benefit. I'm therefore curious about what you're actually trying to accomplish.
Edit after question update:
Putting functions in the Msg is a very bad idea because it goes against the Elm Architecture. It makes the state transition opaque and won't work very well with the debugger. When something goes wrong you can still see the state before and after, but you'll have trouble understanding what happened, and why it happened, because that information is encoded in an opaque function which probably isn't the one it should be.
You'll also have trouble factoring your logic. If you need something to happen only when a certain field updates, you might have to put the logic in the view, or special-case that field by putting the logic for that in update while the rest is in view, for example. Either way, you're on the path to a messy code base.
You should generally use names for messages that describe what happened, not what to do, because that tends to lead to an imperative mindset. Instead of UpdateCell you could call it InputChanged, for example. Then instead of the function you should have an identifier for the field. Ideally a custom type, like InputChanged Name, but even a string will work, though it will be much easier to miss a typo.
So instead of setter functions for each field you'll just case match the message and set the field in the update function:
InputChanged Name value ->
{ patient | name = value }
-- vs
setPatientName : Patient -> String -> Patient
setPatientName patient value =
{ patient | name = value }
Then if you need to clear the sex when the name changes, for example (because reasons...), you can simply do:
InputChanged Name value ->
{ patient | name = value, sex = "" }
The Elm Architecture is good because it makes changes easy and safe, not because it's concise and free of boiler-plate. Good Elm code often has a lot of copy-and-paste, but that's not always bad.

How to add a condition to a match in case ... of

In functional languages, one can add a condition to a branch of a pattern matching: for example, in OCaml:
let value = match something with
| OneThing -> "1"
| Another when condition -> "2"
| _ -> "3"
How to do this in elm? I tried when and if, but nothing worked.
Elm does not have conditionals within pattern matching, probably because the language designers tend to prefer to keep syntax small and simple.
The best you can do is something like this:
let
value =
case something of
OneThing ->
"1"
Another ->
if condition then
"2"
else
...
_ ->
"3"
As an alternative to using an if within the case branch, you might want to instead match against a tuple containing the condition, which would look like this:
let
value =
case ( something, condition ) of
( OneThing, _ ) ->
"1"
( Another, True ) ->
"2"
_ ->
"3"

Pattern match a polymorphic type in a concrete type in Elm

I'm using elm-form: https://github.com/etaque/elm-form/, and I need to pattern match a polymorphic type into a concrete type, however I got the next error:
The pattern matches things of type:
TranslationId
But the values it will actually be trying to match are:
e
Hint: Your type annotation uses type variable `e` which means any type of value
can flow through. Your code is saying it CANNOT be anything though! Maybe change
your type annotation to be more specific? Maybe the code has a problem? More at:
<https://github.com/elm-lang/elm-compiler/blob/0.18.0/hints/type-annotations.md>
The code in question is this:
translateError : ErrorValue e -> String
translateError error =
case error of
InvalidEmail ->
translate English ErrInvalidEmail
Empty ->
translate English ErrEmpty
CustomError PasswordNotMatch ->
translate English PasswordNotMatch
x ->
toString x
ErrorValue type https://github.com/etaque/elm-form/blob/f9480cb8646ebc9f78f13d3a7482c463c5275776/src/Form/Error.elm#L19:
type ErrorValue e
= Empty
| InvalidString
| InvalidEmail
| InvalidUrl
| InvalidFormat
| InvalidInt
| InvalidFloat
| InvalidBool
| InvalidDate
| SmallerIntThan Int
| GreaterIntThan Int
| SmallerFloatThan Float
| GreaterFloatThan Float
| ShorterStringThan Int
| LongerStringThan Int
| NotIncludedIn
| CustomError e
TranslationId type https://github.com/werner/madison-elm/blob/master/src/elm/Translations/Utils.elm#L9:
type TranslationId
= ErrInvalidEmail
| PasswordNotMatch
| ErrEmpty
I came up with a solution, but it looks weird and I'm not sure if it's the right one https://github.com/werner/madison-elm/blob/master/src/elm/Translations/FormErrors.elm#L7:
translateError : ErrorValue e -> String
translateError error =
case error of
InvalidEmail ->
translate English ErrInvalidEmail
Empty ->
translate English ErrEmpty
CustomError e ->
case (toString e) of
"PasswordNotMatch" ->
translate English PasswordNotMatch
x ->
toString x
x ->
toString x
As #reactormonk says, you are not addressing the type variable in your type definition. elm-form provides flexibility in the custom error through this type variable, which you have to supply yourself if you want to use custom errors (if you do not then you can use the variable throughout your code with no problem).
In particular ErrorValue has a type variable e that you need to specify: it does not matter in the code that does not use the CustomError constructor, but does matter in translateError because you are trying to pattern match on CustomError. It looks as though the type you want is TranslationId so you want
translateError : ErrorValue TranslationId -> String
translateError error =
case error of
InvalidEmail ->
translate English ErrInvalidEmail
Empty ->
translate English ErrEmpty
CustomError PasswordNotMatch ->
translate English PasswordNotMatch
x ->
toString x

Partial SQL insert in haskelldb

I just started a new project and wanted to use HaskellDB in the beginning. I created a database with 2 columns:
create table sensor (
service text,
name text
);
..found out how to do the basic HaskellDB machinery (ohhh..the documentation) and wanted to do an insert. However, I wanted to do a partial insert (there are supposed to be more columns), something like:
insert into sensor (service) values ('myservice');
Translated into HaskellDB:
transaction db $ insert db SE.sensor (SE.service <<- (Just $ senService sensor))
But...that simply doesn't work. What also does not work is if I specify the column names in different order, which is not exactly conenient as well. Is there a way to do a partial insert in haskelldb?
The error codes I get are - when I just inserted a different column (the 'name') as the first one:
Couldn't match expected type `SEI.Service'
against inferred type `SEI.Name'
Expected type: SEI.Intsensor
Inferred type: Database.HaskellDB.HDBRec.RecCons
SEI.Name (Expr String) er
When using functional dependencies to combine
Database.HaskellDB.Query.InsertRec
(Database.HaskellDB.HDBRec.RecCons f (e a) r)
(Database.HaskellDB.HDBRec.RecCons f (Expr a) er),
etc..
And when I do the 'service' as the first - and only - field, I get:
Couldn't match expected type `Database.HaskellDB.HDBRec.RecCons
SEI.Name
(Expr String)
(Database.HaskellDB.HDBRec.RecCons
SEI.Time
(Expr Int)
(Database.HaskellDB.HDBRec.RecCons
SEI.Intval (Expr Int) Database.HaskellDB.HDBRec.RecNil))'
against inferred type `Database.HaskellDB.HDBRec.RecNil'
(I have a couple of other columns in the table)
This looks really like 'by design', unfortunately :(
You're right, that does look intentional. The HaskellDB.Query docs show that insert has a type of:
insert :: (ToPrimExprs r, ShowRecRow r, InsertRec r er) => Database -> Table er -> Record r -> IO ()
In particular, the relation InsertRec r er must hold. That's defined elsewhere by the recursive type program:
InsertRec RecNil RecNil
(InsertExpr e, InsertRec r er) => InsertRec (RecCons f (e a) r) (RecCons f (Expr a) er)
The first line is the base case. The second line is an inductive case. It really does want to walk every element of er, the table. There's no short-circuit, and no support for re-ordering. But in my own tests, I have seen this work, using _default:
insQ db = insert db test_tbl1 (c1 <<- (Just 5) # c2 << _default)
So if you want a partial insert, you can always say:
insC1 db x = insert db test_tbl1 (c1 <<- (Just x) # c2 << _default)
insC2 db x = insert db test_tbl2 (c1 << _default # c2 <<- (Just x))
I realize this isn't everything you're looking for. It looks like InsertRec can be re-written in the style of HList, to permit more generalization. That would be an excellent contribution.