Reputation: 7092
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:
Any tips on how to do this c# to f# conversion?
Upvotes: 1
Views: 232
Reputation: 89
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! :)
Upvotes: 2
Reputation: 3502
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.
Upvotes: 3
Reputation: 62975
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)
Upvotes: 4