SkyeBoniwell
SkyeBoniwell

Reputation: 7092

c# to f# conversion with interfaces and linq statements

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

Answers (3)

John Azariah
John Azariah

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

Isaac Abraham
Isaac Abraham

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

ildjarn
ildjarn

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

Related Questions