Generating F# code
Asked Answered
H

4

10

T4 is the "official" code generation engine for C#/VB.NET. But F# doesn't support it (this is from April, but I couldn't find any newer mentions). So what is a good way to generate F# code?

EDIT:

I want to implement 2-3 finger trees in F#. I already have implemented them in C#, so this should be a nice comparison. The "digits" and nodes of the tree can be represented as arrays, so

type 't FingerTree = Empty | Single of 't | Deep of 't array * (('t FingerTree) array) lazy * 't array

However, the maximum size of these arrays is very small, so it'd be nice to have

type 't Digit = Digit1 of 't | Digit2 of 't*'t | Digit3 of 't*'t*'t | Digit4 of 't*'t*'t*'t
type 't Node = Node2 of 't FingerTree * 't FingerTree | Node3 of 't FingerTree * 't FingerTree * 't FingerTree 
type 't FingerTree = Empty | Single of 't | Deep of 't Digit * ('t Node) lazy * 't Digit

to avoid bounds checking, etc.

But then writing all functions on Digit and Node by hand becomes more difficult, and it's better to generate them. And a T4-like approach looks perfect for it...

Hemihedral answered 2/2, 2010 at 8:10 Comment(1)
I'll just throw this out there. It's something I've hacked together recently.Sisterhood
W
8

Because F# doesn't support custom tools in solution explorer, you can place your T4 files in a C# or Visual Basic project and redirect their output to your F# project. Here is how you can do it with T4 Toolbox:

<#@ template language="C#" hostspecific="True" debug="True" #>
<#@ output extension="txt" #>
<#@ include file="T4Toolbox.tt" #>
<#
    FSharpTemplate template = new FSharpTemplate();
    template.Output.Project = @"..\Library1\Library1.fsproj";
    template.Output.File = "Module2.fs";
    template.Render();
#>
<#+
class FSharpTemplate: Template
{
    public override string TransformText()
    {
#>
// Learn more about F# at http://fsharp.net

module Module2
<#+
        return this.GenerationEnvironment.ToString();
    }
}

#>
Whole answered 4/2, 2010 at 12:17 Comment(2)
Unfortunately this will add Module2.fs to the bottom of Library1.fsproj and order of source files is important in F# :-(Whereas
"Because F# doesn't support custom tools in solution explorer", not sure what you mean by that, as we use custom tools in our F# projects all the time: FsYacc, FsLex, test generators and other MSbuild extensions. In regards to the order of evaluation, either include the files in the right order in the *.fsproj files (and don't update fsproj files automatically), or add them at the bottom and make sure that dependencies are resolved by preceding, non-auto-generated files.Aurify
U
6

It depends what your trying to do. While it's an approach that's not really suitable for generating templates in the way many T4 examples show, in general I would recommend designing a "combinators library" [1] for code generation or language oriented programming tasks in F#. The idea is to design some combinators to represent the code you're try to generate, generating F# source text from combinators, then compiling this via the code DOM.

However often it would be easier simply to write an interpreter for your combinators rather than generating code.

Good examples of combinators in F# are:

[1] http://en.wikipedia.org/wiki/Combinator_library

Undertake answered 2/2, 2010 at 9:13 Comment(2)
Just to clarify - you're suggesting writing a combinator library that outputs something like a discriminated union hierarchy that can be translated to source code, right? As opposed to using FParsec itself to generate code, I mean. I just want to make sure there's not some hidden feature of FParsec I was unaware of...Spool
Yes, a good place to start would be a union type that could be translated into F# code. FParsec was just an example of a combinator library, it does not generate F# code, at least as far as I'm aware.Undertake
S
5

I looked around at various options, and ended up for my relatively simple and static code-generation needs using a *.fsx script that uses a TextWriter with fprintf to write out the generated F# code.

I actually do use FParsec for some parsing work, but as I'm not translating from some other syntax into F#, the two pieces have little to do with each other.

Spool answered 2/2, 2010 at 22:28 Comment(1)
Sure do love those downvotes without a comment to say what's wrong with my answer. I learn so much from them!Spool
S
1

I mostly agree with Robert (though there are certainly some situations where using T4 from F# could be very useful). Anyway, maybe it would be interesting to know why do you want to generate F# code? Then we could maybe suggest some typical functional solution to the problem :-).

Scutage answered 2/2, 2010 at 21:45 Comment(3)
I'm afraid I don't have any good idea how to solve this elegantly - I had a similar problem some time ago and didn't find any good way. Even if T4 (or similar) worked, it would still break all F# edit-time type checking, which would be quite annoying. In OCaml, this is solved by Campl4 (en.wikipedia.org/wiki/Camlp4), but there is no equivalent thing for F# (and I'm afraid there is only limited need for this, especially compared with other possible F# improvements).Scutage
Regarding arrays vs. tuples - I think that the performance of arrays (with bounds checking) may not be as bad (afterall, the CLR could avoid checks in many cases). However, you could also try functional solution using F# lists (if you can avoid direct indexing), because very small lists should be quite fast (but I don't have any numbers, unfortunately).Scutage
In this case I expect CLR can avoid most checks, but not all. Of course, to test the actual overhead I need a version which avoids them, first...Hemihedral

© 2022 - 2024 — McMap. All rights reserved.