Skip to content

[REQ] FSharp (F#) server stub based on record types #2342

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
viktor-ferenczi opened this issue Mar 9, 2019 · 20 comments
Closed

[REQ] FSharp (F#) server stub based on record types #2342

viktor-ferenczi opened this issue Mar 9, 2019 · 20 comments

Comments

@viktor-ferenczi
Copy link

viktor-ferenczi commented Mar 9, 2019

F#+Giraffe backend stub generator.

Alternatives attempted:

  • OpenAPITypeProvider: Type provider can be used both during design time (IDE) and runtime. It generates C# classes on the fly, they are composing naturally in F#. Having to reload IDE every time changes are made to the API file. Some metadata (path, info) are also exposed. It is an excellent type provider, but we could have something more composable in F#.
  • Using model classes from C# ASP.NET or NancyFX server stub: No reload issue on changes. Generates code not directly used in the F# project. It generates C# classes not composing naturally in F#.

In the above solutions classes are accessible from F# via the common platform (.NET CLI).

I suggest a new backend generating F# record types like:

type Employee = 
	{ name: string
	; salary: float 
	}

Record type would provide familiar access with dot notation, plus being accessible from C# code if needed.

These generated accessors would be composable in functional code:

type Employee with
        static member getName employee = employee.name
	static member getSalary employee = employee.salary
	static member withName value employee = {employee with name = value}
	static member withSalary value employee = {employee with salary = value}

The updater has the object as its last parameter, which is good practice in functional programming.

Example usage:

let totalSalary = List.sum Employee.getSalary employees
let employeeCount = List.length employees
let equalSalary = totalSalary / (float employeeCount)
let equalEmployees = List.map (Employee.withSalary equalSalary) employees

Future versions of F# may gain new syntax for convenient property access, which will simplify the getter to: _.salary

Despite this the composable update functions (like withSalary) will remain useful.

I suggest using the Giraffe Web framework in the generated backend stub, since it generally results in a clean default implementation. Giraffe has a SampleApp solution which could be used as the basis of the generated one.

@viktor-ferenczi viktor-ferenczi changed the title [REQ] FSharp (F#) server stub generating record types [REQ] FSharp (F#) server stub based on record types Mar 9, 2019
@wing328
Copy link
Member

wing328 commented Mar 9, 2019

@viktor-ferenczi Sounds good to me 👍

Would you have time to contribute the new F# server stub generator?

We can work with you to create the new generator similar to what we've done before: https://github.com/OpenAPITools/openapi-generator/projects?query=is%3Aclosed

@viktor-ferenczi
Copy link
Author

That's exactly my plan. It is just good practice to add a request first and wait for some discussion, so I start in the right direction. I already have the openapi-generator project set up in IntelliJ IDEA.

Hint needed: How can I limit the build to a single backend? (For development speed.)

@wing328
Copy link
Member

wing328 commented Mar 10, 2019

@viktor-ferenczi when you recompile the project, use mvn clean package -DskipTests instead.

@nmfisher
Copy link
Contributor

@viktor-ferenczi I'm happy to help contribute to this. Have you opened a repository anywhere?

@wing328
Copy link
Member

wing328 commented Mar 24, 2019

To facilitate collaboration among F# developers, what about creating a branch "f-sharp-server" in the official OpenAPI Generator repo so that all F# developers can contribute to this branch to move the project forward?

@nmfisher
Copy link
Contributor

@wing328 Sounds good to me!

@wing328
Copy link
Member

wing328 commented Mar 24, 2019

Done: https://github.com/OpenAPITools/openapi-generator/tree/f-sharp-server

@nmfisher
Copy link
Contributor

@viktor-ferenczi are you at a stage where you can push to this branch? I'm starting from a clean slate, and ideally I'd prefer to avoid duplicating work that you've already done.

@viktor-ferenczi
Copy link
Author

@nmfisher I haven't started on this yet. (High workload on my regular job.)

@wing328
Copy link
Member

wing328 commented Mar 25, 2019

@viktor-ferenczi no worries. I'll create the base class and the templates so that people can start patching it to support the F# server stub generator.

@nmfisher
Copy link
Contributor

No problem @viktor-ferenczi , I am working on this at the moment and aim to have something available on the f-sharp-server branch in the next few days.

@wing328
Copy link
Member

wing328 commented Mar 25, 2019

@nmfisher I'll wait for your PR then.

@nmfisher
Copy link
Contributor

Fork is at https://github.com/nmfisher/openapi-generator/tree/f-sharp-server if anyone wants to track WIP before issuing a PR.

@viktor-ferenczi
Copy link
Author

@nmfisher I've just cloned your branch.

From Model.mustache:

module Model =
    type Definitions = OpenAPIV3Provider<"openapi.yaml">

Is it a placeholder?

The whole point of the fsharp-server backend is to avoid having to use OpenAPIV3Provider on the server side, since that does not play well with IDEs during development. (See ticket description above.)

@nmfisher
Copy link
Contributor

Right now, it's just a placeholder.

I understand your point about IDE restarts (though that seems to be a general issue around type providers - I also experimented with a new type provider for this branch but decided not to bother).

Seems a shame to re-do all the good work that OpenAPIV3Provider has already done though?

@viktor-ferenczi
Copy link
Author

Yeah, it is a shame.

But there is another reason: A type provider is relatively opaque, while generated source is transparent. It is possible to copy part of the generated type declarations. For example, to come up with an in-memory record containing a subset of fields. This is often useful in practice.

@viktor-ferenczi
Copy link
Author

@nmfisher Good job on starting the backend! In case you would like me to write the template for the model record type generation, please let me know.

@nmfisher
Copy link
Contributor

Thanks @viktor-ferenczi - I'm actually going through the model record template at the moment. Will let you know if I have any questions.

@wing328
Copy link
Member

wing328 commented Apr 22, 2019

Thanks for the PR by @nmfisher

@viktor-ferenczi when you've time, I wonder if you can review. Thanks!

@nmfisher
Copy link
Contributor

@wing328 suggest you close this issue as the PR has been merged and no activity since 2019.

@wing328 wing328 closed this as completed May 31, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants