Dzoukr / Dapper.FSharp

Lightweight F# extension for StackOverflow Dapper with support for MSSQL, MySQL, PostgreSQL, and SQLite
MIT License
374 stars 35 forks source link

In a where condition, why (according to docs) is (+) considered 'AND' and (*) is 'OR'? #7

Closed travis-leith closed 4 years ago

travis-leith commented 4 years ago

This is swapped around from typical usage, no?

Dzoukr commented 4 years ago

I got inspired by other F# libraries, where + is operator for AND and * for OR.

I my dream world we would have operator overload with && and ||.

Zaid-Ajaj commented 4 years ago

This is swapped around from typical usage, no?

Theoretically speaking in Algebraic data types, it is indeed the other way around: Unions are "sum types" for example Shape = Circle OR Rectangle OR Triangle

Tuples/records are "product types" such asType1 * Type2 * Type3 ... TypeN where each value of such type has to have Type1 AND Type2 AND Type3 ... TypeN

Notice the association of "sum"/"or" and "product"/"and" in these definitions. Maybe something to consider in the next major release :wink:

Dzoukr commented 4 years ago

I am not sure the analogy with sum/product types works here. I understand where conditions as filters and when you want to combine them together, I see + sign a natural way of adding them together.

Looking at @isaacabraham's code at https://github.com/CompositionalIT/kibalta/blob/master/src/Kibalta.fs#L57-L60 I am sure I am not the only one 😄

Anyway, I will not change this even in major version. This is so dangerous breaking change that even with hundreds of documentation warnings, this could cause some users real trouble.

Zaid-Ajaj commented 4 years ago

Anyway, I will not change this even in major version. This is so dangerous breaking change that even with hundreds of documentation warnings, this could cause some users real trouble.

Both ways are good, really! It was more of a suggestion :smile:

In my dream world we would have operator overload with && and ||.

Actually I was playing a little bit with this, it might be quite doable with a nice API using F# quotations but I need to flesh it out to see if it actually works. Right now I have something that looks like this

Sql.where (fun column value -> column "title" = value "Hello" && column "updated" > value DateTime.Now)

probably it can be simplified for single columns into something like this

Sql.where (fun column -> column "updated" > DateTime.Now)

Sql.where (fun column -> not (column "is_active"))

Sql.where (fun column -> String.IsNullOrWhiteSpace (column "description"))

But then you get a giant quotation expression back from which you have to extract the values and logical expressions. It is quite challenging I think but still doable. Maybe we can discuss this over slack (or I could play with it next stream to figure out how to make it work and then fail miserably, live :joy:)

Dzoukr commented 4 years ago

But then you get a giant quotation expression back from which you have to extract the values and logical expressions.

Now I'm scared 😂 But on a serious note, this is what SQLProvider probably does as well. I am thinking about tradeoffs for adding value to library users vs adding complexity.

Zaid-Ajaj commented 4 years ago

I am thinking about tradeoffs for adding value to library users vs adding complexity.

I personally think that for anything more complex than what the library allows, one can simply go back to raw SQL as a perfectly good and accurate way to describe the SQL one wants to write :wink:

I just wanted to let you know that it is possible if you want to play with it more :smile:

Dzoukr commented 4 years ago

I just wanted to let you know that it is possible if you want to play with it more

You are famous in the community for making things happen 😄 so I am not surprised at all. 😉

Zaid-Ajaj commented 4 years ago

Thanks Roman :heart: to be fair I only write a lot of code til a subset of it works and I throw away the rest hahah :joy:

In any case, here is a sample code you can try out if you want to implement the quotations thingy:

open FSharp.Quotations
open System

type Sql =
    static member where([<ReflectedDefinition>] f: Expr<(string -> 'T) -> bool>) = 
        printfn "%A" f
    static member where([<ReflectedDefinition>] f: Expr<(string -> int) -> (obj -> int) -> bool>) = 
        printfn "%A" f

Sql.where (fun column -> column "title" = "Hello")

Sql.where (fun column value -> column "title" = value "Hello" && column "updated" > value DateTime.Now)

but instead of printfn "%A" f you would actually traverse the expression and transform it into something else (i.e. SQL where clause) Also I am not sure how well these static members play with the existing CE

travis-leith commented 4 years ago

Closing this as it seems to be decided to leave things as is.