Is it possible to use lambda-style querying of IQueryable objects in F#, instead of query expressions? Something like:
type schema = SqlDataConnection<"Data Source=(local);Initial Catalog=MyDatabase;Integrated Security=true;">
let db = schema.GetDataContext()
let q = db.MyTable |> Seq.filter (fun r -> r.id < 100) |> Seq.take 10
let result = q |> List.ofSeq
When I profile this it is doing select * from MyTable so I assume the filter and take are being executed on IEnumerables not IQueryables?
Or is the only way to fix this to use query {} without lambdas?
The reason is that Seq.toList calls the data struncture GetEnumerator() and there is something like this inside the type (pseudo, not the actual source):
type SqlDataConnection<...>(...) =
interface seq<'T> with
member __.GetEnumerator() = executeSQL()
If you want to operate with IQueryable instead of IEnumerable, there is the query-syntax in F#:
query {
for r in db.MyTable do
where (r.id < 100)
take 10
} |> Seq.toList
More details:
https://learn.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/query-expressions
Related
I'm using quite a lot this piece of code:
let inline (||>) (a: 'a option) (b: 'a -> unit) = if a.IsSome then b a.Value
so I can do things like
myData ||> DoSomethingWithIt
without having to test if myData is Some or None since there are many functions that don't generally need to test for an option. This avoid to put the test in the function itself.
I would like to extend this to methods of a type where I could do like C#'s:
myData?.DoSomethingWithIt
essentially replacing:
if myData.IsSome then myData.Value.DoSomethingWithIt
with some syntactic sugar.
but I have no idea how I could do the operator so that it allows to get access to the type's method in the expression. Is that possible in F#?
I'm also open to learn about why it could be a bad idea if it is :)
Depending on your return type of DoSomethingWithIt the F# library offers a few standard functions for working with Options that can be turned into operators.
let x = Some 1
let aPrinter a = printfn "%i" a
let add1 a = a + 1
let (|?>) opt f = Option.iter f opt
let (|??>) opt f = Option.map f opt
x |?> aPrinter
let y = x |??> add1
You can also consider redefining your DoSomethingWithIt to work with an option by partial application.
let DoSomethingWithIt' = Option.iter DoSomethingWithIt
let something' = Option.iter (fun (b:B) -> b.DoSomethingWithIt()) //For instance methods
That may end up being a lot of work depending how many functions you are dealing with.
Ultimately you shouldn't try to hide the fact you are working with Options. By making something an Option you are telling the compiler that you aren't sure whether it exists or not. It is trying to help you by forcing you to deal with the None case. If there are lots of cases in your code where you know your Option is Some then there's probably a larger architectural issue in your code and you should try to lift all your Option<'T> to just T prior to doing work with them. e.g.:
let lift xs =
[
for x in xs do
match x with
| Some x -> yield x
| None -> ()
]
Have a look at Option.iter. It has the same signature as your operator.
There is no analogical syntax for such constructions but F# have alternatives.
The easiest way is to use FSharpx.Extras library and FSharpx.Option.maybe computation expression which will allow you to use Option related operations.
open FSharpx.Option
let a = Some 1
let b = maybe {
let! v = a
return v + 3
} // b is (Some 4)
let c : int option = None
let d = maybe {
let! v = c
return v + 3 // this line won't be reached
} // d is None
I believe that the ?. operator in c# is a syntactic sugar that hides the if statement checking for null before invoking a member of the type. Even if you could make it work the way you plan, I feel that it would go against the FP principles and could cause more problems down the line.
The Option module contains probably most of what you need already. The iter function allows to call a function on the value of the Option if that value is present (Some).
If you have situation that your input parametes can be nulls, but not options, you can use the Option.ofObj function that will convert the parameter to an Option with Some if the parameter is not null, else None.
So assuming that your function DoSomethingWithit accepts a string and returns unit:
let DoSomethingWithIt = //(string -> unit)
printf "%s; "
You can use this more verbose syntax to (for example) iterate over nullable values in your list:
let lst = [ "data"; "data 2"; null; "data3" ]
lst
|> List.iter (fun v -> v |> Option.ofObj |> Option.iter DoSomethingWithIt)
Alternatively you can compose the Optioni.ofObj and Option.iter DoSomethingWithIt functions and do something like
let SafeDoSomethingWithIt = //(string -> unit)
Option.ofObj >> Option.iter DoSomethingWithIt
This gives you safe invocation:
let lst2 = [ "data"; "data 2"; null; "data3" ]
lst2
|> List.iter SafeDoSomethingWithIt
You can generalize the combination of the functions returning unit (but not only)
let makeSafe fn =
Option.ofObj >> Option.iter fn
Then you can create a series of safe functions:
let SafeDoSomethingWithIt = makeSafe DoSomethingWithIt
let safePrint = makeSafe (printf "%s; ")
//...etc
Then this still works:
lst2
|> List.iter SafeDoSomethingWithIt
lst2
|> List.iter safePrint
You can also write a wrapper for functions returning values using Option.bind function.
let makeSafeReturn fn = //(string -> string option)
Option.ofObj >> Option.bind fn
I have a function processing a DataTable looking for any row that has a column with a certain value. It looks like this:
let exists =
let mutable e = false
for row in dt.Rows do
if row.["Status"] :?> bool = false
then e <- true
e
I'm wondering if there is a way to do this in a single expression. For example, Python has the "any" function which would do it something like this:
exists = any(row for row in dt.Rows if not row["Status"])
Can I write a similar one-liner in F# for my exists function?
You can use the Seq.exists function, which takes a predicate and returns true if the predicate holds for at least one element of the sequence.
let xs = [1;2;3]
let contains2 = xs |> Seq.exists (fun x -> x = 2)
But in your specific case, it won't work right away, because DataTable.Rows is of type DataRowCollection, which only implements IEnumerable, but not IEnumerable<T>, and so it won't be considered a "sequence" in F# sense, which means that Seq.* functions won't work on it. To make them work, you have to first cast the sequence to the correct type with Seq.cast:
let exists =
dt.Rows |>
Seq.cast<DataRow> |>
Seq.exists (fun r -> not (r.["Status"] :?> bool) )
Something like this (untested):
dt.Rows |> Seq.exists (fun row -> not (row.["Status"] :?> bool))
https://msdn.microsoft.com/visualfsharpdocs/conceptual/seq.exists%5b%27t%5d-function-%5bfsharp%5d
I'm trying to convert this c# method that uses interfaces and Linq to f#:
public static IEnumerable<ModelEngines> CurrentEngineBuilds(IEnumerable<CurrentModels> thisYearModels, DateTime startRunDate, DateTime endRunDate)
{
var engineOptions =
currentModelYear.SelectMany(c => c.Engines)
.Where(e => e.EngineProdStartDate >= startRunDate & e.EngineProdStopDate <= endRunDate);
return engineOptions;
}
In the method above, I'm returning a collection of engineOptions - type is IEnumerable. I'm filtering the collection so that the collection only has engineOptions with a certain production range from the current model year.
I have this rough, non-working, translation below:
let CurrentEngineBuilds : <IEnumerable<ModelEngines>> (thisYearModels : IEnumerable<CurrentModels>) (startRunDate : DateTime) (endRunDate : DateTime) =
query {
for engineOptions in currentModelYear.engineOptions do
where (engineOptions.EngineProdStartDate >= startRunDate and engineOptions.EngineProdEndDate >= endRunDate )
select (engineOptions)
}
engineOptions
This task has become more complicated than I had anticipated. I've gone through a few f# tutorials to get through the basics, but it seems anything to do with interfaces and/or linq is pretty difficult.
Some issues I have:
How the heck to you work with interfaces in f#?
Is there an f# equivilent of the SelectMany method?
Any tips on how to do this c# to f# conversion?
IEnumerable<'T> is named seq<'T> in F#. I don't see any reason to use LINQ query expressions in this case; the more idiomatic F# translation would use the Seq module:
let CurrentEngineBuilds thisYearModels startRunDate endRunDate =
thisYearModels
// v--- SelectMany
|> Seq.collect (fun c -> c.Engines)
// v--- Where
|> Seq.filter (fun e ->
e.EngineProdStartDate >= startRunDate && e.EngineProdStopDate <= endRunDate)
If the type of thisYearModels is not deduced automatically, you can annotate it in the function signature:
let CurrentEngineBuilds (thisYearModels:seq<CurrentModels>) startRunDate endRunDate = ...
or in the body:
let CurrentEngineBuilds thisYearModels startRunDate endRunDate =
(thisYearModels:seq<CurrentModels>)
|> Seq.collect (fun c -> c.Engines)
|> Seq.filter (fun e ->
e.EngineProdStartDate >= startRunDate && e.EngineProdStopDate <= endRunDate)
// or
let CurrentEngineBuilds thisYearModels startRunDate endRunDate =
thisYearModels
|> Seq.collect (fun (c:CurrentModels) -> c.Engines)
|> Seq.filter (fun e ->
e.EngineProdStartDate >= startRunDate && e.EngineProdStopDate <= endRunDate)
You can actually use LINQ in F# without any problems whatsoever. Here's an almost direct port of your original sample (I'm assuming that currentModelYear is a type and you meant thisYearModels)
open System.Linq
let currentEngineBuilds(thisYearModels:seq<CurrentModels>, startRunDate, endRunDate) =
let engineOptions =
thisYearModels
.SelectMany(fun e -> e.Engines)
.Where(fun e -> e.StartDate >= startRunDate && e.StopDate <= endRunDate)
engineOptions // return keyword not required
For the purposes of this post, you can think of seq<T> as a shorthand for IEnumerable<T>.
Having said that, I think that you'll find the Seq module a better fit for F# as it's designed to take advantage of F# language features such as Tuples, and can assist in the type inference process.
The |> or "pipe" operator is F#'s idiomatic way of applying a function to a value.
v |> f is the same as f v as pointed out earlier.
Monadic chaining, on the other hand, is quite another matter entirely. You're using monadic chaining in the query computation expression in the original post, and it is also idiomatic.
Hope that helps to keep those two (fairly complex) concepts apart in your understanding! :)
I have a method which works fine:
member this.GetItems =
let db = dbSchema.GetDataContext()
let getQuery =
query {
for row in db.ItemsTable do
select row
}
getQuery
|> Seq.map (fun e -> new Item(e.ItemId, e.ItemName ))
|> Seq.toArray
It make the select query to database, transforms each record to object and returns an array of such objects.
I want to split this method to two. First one must be more general. It gets query and function to transform record to object.
Here is my code:
member private this.ExecuteSelectQuery(query, transform_function) =
let db = dbSchema.GetDataContext()
query
|> Seq.map transform_function
|> Seq.toArray
member this.GetItems =
let db = dbSchema.GetDataContext()
this.ExecuteSelectQuery
query {
for row in db.ItemsTable do
select row
}
(fun e -> new Item(e.ItemId, e.ItemName ))
But I get the errors:
In this expression required type 'a * ('b -> 'c) but there is a type QueryBuilder
This construction can be used only in the case of the method For
How can I fix it ?
Updated:
member private this.ExecuteSelectQuery query transform_function =
let db = dbSchema.GetDataContext()
query
|> Seq.map transform_function
|> Seq.toArray
member this.GetItems =
let db = dbSchema.GetDataContext()
this.ExecuteSelectQuery
query {
for row in db.ItemsTable do
select row
}
(fun e -> new Item(e.ItemId, e.ItemName ))
The member function ExecuteSelectQuery expects the arguments as a tuple, so you have to use parentheses.
member private this.ExecuteSelectQuery(query, transform_function) =
query
|> Seq.map transform_function
|> Seq.toArray
member this.GetItems =
use db = dbSchema.GetDataContext()
this.ExecuteSelectQuery (
query {
for row in db.ItemsTable do
select row
},
(fun e -> new Item(e.ItemId, e.ItemName )))
Please consider also the use keyword for binding of DataContext instance to correctly implement the disposable pattern. I've done it in my example.
I'm writing a quick DB perf test, and chose F# so I can get more practice.
I've created a method, measureSelectTimes, which has the signature Guid list * Guid list -> IDbCommand -> TimeSpan * TimeSpan.
Then, I call it:
let runTests () =
let sqlCeConn : IDbConnection = initSqlCe() :> IDbConnection
let sqlServerConn : IDbConnection = initSqlServer() :> IDbConnection
let dbsToTest = [ sqlCeConn; sqlServerConn ]
let cmds : seq<IDbCommand> = dbsToTest |> Seq.map initSchema
let ids : seq<Guid list * Guid list> = cmds |> Seq.map loadData
let input = Seq.zip ids cmds
let results = input |> Seq.map (fun i -> measureSelectTimes (fst i) (snd i))
// ...
I've annotated explicitly with types to clarify.
What I can't figure out is how to call measureSelectTimes without the lambda. I'd like to partially apply the ids to it like this: ids |> Seq.map measureSelectTimes but then I don't know what to do with the resulting partially applied functions to then map onto the cmds. What's the syntax for this?
You can use Seq.map2:
Seq.map2 measureSelectTimes ids cmds
Or
(ids, cmds) ||> Seq.map2 measureSelectTimes
Your measureSelectTimes function takes two arguments as separate arguments, but you instead need a function that takes them as a tuple. One option is to just change the function to take a tuple (if it is logical for the arguments to be tupled).
Alternative, you can write a cobinator that turns a function taking two arguments into a function taking tuple. This is usually called uncurry and it exists in some functional language:
let uncurry f (a, b) = f a b
Then you can write:
input |> Seq.map (uncurry measureSelectTimes)
This looks okay for a simple use like this, but I think that using combinators too much in F# is not a good idea as it makes code difficult to read for less experienced functional programmers. I would probably write something like this (because I find that more readable):
[ for (time1, time2) in input -> measureSelectTimes time1 time2 ]
One approach is to change the signature of measureSelectTimes to
(Guid list * Guid list) * IDbCommand -> TimeSpan * TimeSpan
Then you can change the map call to
let results = input |> Seq.map measureSelectTimes
// or
let results = Seq.map measureSelectTimes input