Open al1-ce opened 4 months ago
For comparison:
/// Current:
#if d
native {
module hellofu;
}
#endif
public static class HelloFu {
/// Return hello message
public static string GetMessage() {
#if d
native {
import std.stdio;
writeln("This is a D injection!");
}
#elif js
native {
console.log("This is a JS injection!");
}
#endif
#if c
return "Hello C!";
#else
return "Hello World!";
#endif
}
}
---------------------------------------------------------
/// Proposed:
flag(D) native {
module hellofu;
}
public static class HelloFu {
/// Return hello message
public static string GetMessage() {
flag(D) native {
import std.stdio;
writeln("This is a D injection!");
}
flag(JS) native {
console.log("This is a JS injection");
}
flag(C) {
return "Hello C!"
} else {
return "Hello World!";
}
}
}
D's version
has one huge disadvantage: non-D programmers are not familiar with it. #if
is instantly recognized by C, C++ and C# programmers.
I don't expect #if
do be often used in Fusion code, so saving a few keystrokes here and there doesn't seem to justify syntax unfamiliar to most programmers. The whole fut
codebase doesn't have a single #if
or native
.
I could consider adding an argument to native
that specifies the target language, because native
is normally used under a conditional compilation anyway.
D's version has one huge disadvantage: non-D programmers are not familiar with it. #if is instantly recognized by C, C++ and C# programmers.
Tbh I don't think most of C# programmers even know about #if
since C# is interpreted. Conditional compilation is used in... compiled languages. But I might be wrong about C#.
I don't expect #if do be often used in Fusion code
Most programmers right now are python, js/ts (or web in general), rust (which has attributes and macros as way for conditional compilation). And imo C/C++ programmers wouldn't use fusion, unless they really need (as of right now) JavaScript and TypeScript as from all other languages have at least some sort of C interop.
so saving a few keystrokes here and there doesn't seem to justify syntax unfamiliar to most programmers.
Isn't native
an unfamiliar syntax? It is easy to figure out, since it's literally called native, but still.. Also every language has some sort of it's own syntax which is unfamiliar to some/many programmers. There's always some learning curve, so, personally, I don't think it's an issue.
The whole fut codebase doesn't have a single #if or native.
That's fair, but it's a single project. There are many cases where one would want a bunch of native blocks: attributes (c++, c#, java, d...), unittests (d (idk if there are others with native unittest support)), lang-specific libraries, inline assembly (c, c++, d). For conditional compilation it can be just doing different thing in different languages. And as amount of languages grow there will be more and more edge cases where #ifs and native blocks would become nessesary
I could consider adding an argument to native that specifies the target language, because native is normally used under a conditional compilation anyway.
That is fair, but what would be syntax? There are basically two ways I see right now without changing much,
First falls under your unfamiliar syntax argument and second sticks out enough to prompt programmer to look at docs, so... I guess second?
D's version has one huge disadvantage
Oh, on that note there's also a static if () {}
, which would maybe satisfy you more, as if instead of #if
it'd be something similar to static if
, but using different keyword maybe?
Also another thing why I made this proposal is readability. C-style preprocessor statements take a lot of space since they're mostly line-based (and I personally stand for OTBS).
Here's an argument, which would be eliminated if to integrate (optional) flags into native blocks
// currently
int func() {
#if FLAG
native {
// code
}
#endif
}
// Or
int func() {
#if FLAG
native {
// code
}
#endif
}
// Or
int func() {
#if FLAG
native {
// code
}
#endif
}
// what I was proposing
int func() {
flag(FLAG) native {
// code
}
}
// if to integrate flags into native
int func() {
native FLAG {
// code
}
}
For me in current way all three options are bad way to write code. Both my proposed way and native flags way are more natural, so, if not have better #if
syntax, then I heavily vote for flags in native!
If you'd rather go with flags in native, then please rename this issue, just so it's easier to track
D's version has one huge disadvantage: non-D programmers are not familiar with it. #if is instantly recognized by C, C++ and C# programmers.
Tbh I don't think most of C# programmers even know about
#if
since C# is interpreted. Conditional compilation is used in... compiled languages. But I might be wrong about C#.
I don't know what's your experience with C#, but from 2004 to 2014 I worked full-time as a C# developer, on a team of a dozen developers. #if
is much rarer in C# than in C, but we were aware of it.
I don't expect #if do be often used in Fusion code
Most programmers right now are python, js/ts (or web in general), rust (which has attributes and macros as way for conditional compilation). And imo C/C++ programmers wouldn't use fusion, unless they really need (as of right now) JavaScript and TypeScript as from all other languages have at least some sort of C interop.
Could you elaborate on your point of view?
Fusion is an alternative to C interop and I expected the target audience to be people tired of developing libraries in C with tons of bindings. Why would a web developer need Fusion at all?
so saving a few keystrokes here and there doesn't seem to justify syntax unfamiliar to most programmers.
Isn't
native
an unfamiliar syntax?
I was referring to #if
vs version
. #if
being used by C, C++ and C# programmers, version
only by D programmers.
native
is a different story, because no mainstream language is designed for transpiling. The closest I can think of is:
native
(same keyword!)asm
statement - does kind of what native
in Fusion, however what you put in Fusion is certainly not assembly languageIt is easy to figure out, since it's literally called native, but still.. Also every language has some sort of it's own syntax which is unfamiliar to some/many programmers. There's always some learning curve, so, personally, I don't think it's an issue.
The whole fut codebase doesn't have a single #if or native.
That's fair, but it's a single project. There are many cases where one would want a bunch of native blocks: attributes (c++, c#, java, d...), unittests (d (idk if there are others with native unittest support)), lang-specific libraries, inline assembly (c, c++, d). For conditional compilation it can be just doing different thing in different languages. And as amount of languages grow there will be more and more edge cases where #ifs and native blocks would become nessesary
Is your Fusion codebase open source?
I have eight open source projects (fut being one of) and there's exactly one use of native
in one project. And it's just a quick hack that I was too lazy to design properly.
The expected integration with the target languages is to use Fusion-generated classes, sometimes subclassing them, not add much native
code in Fusion source files.
I could consider adding an argument to native that specifies the target language, because native is normally used under a conditional compilation anyway.
That is fair, but what would be syntax? There are basically two ways I see right now without changing much,
- native(FLAG) {}
- native FLAG {}
First falls under your unfamiliar syntax argument and second sticks out enough to prompt programmer to look at docs, so... I guess second?
First one looks good. I don't know if FLAG should be a preprocessor symbols expression or maybe a list of languages? (lower/uppercase? ||
or comma-separated?)
D's version has one huge disadvantage
Oh, on that note there's also a
static if () {}
, which would maybe satisfy you more, as if instead of#if
it'd be something similar tostatic if
, but using different keyword maybe?
This is again D syntax. As much as I have sympathy for D, I don't want to clone its syntax.
Also another thing why I made this proposal is readability. C-style preprocessor statements take a lot of space since they're mostly line-based (and I personally stand for OTBS).
I find it an advantage: if conditional compilation looks bad, design your code to avoid it.
If you'd rather go with flags in native, then please rename this issue, just so it's easier to track
I'd be happy to see your usecases!
Fusion wasn't designed in vain. From the very beginning, it served as an implementation language for working projects.
native
was an easy addition, but if abused, it would totally obfuscate Fusion code.
Ok, I see your points (with a bit clearer mind) and I'd say let's stick to thinking about improving native
syntax and keep #if
as is.
So, potential solutions are:
// i assume capital flags here
// because they are easier
// to differentiate from normal
// syntax and symbols
// more like a preprocessor expr
native C {}
// more functional approach
native(C) {}
// if to allow a list of languages
native(C || CPP || OPENCL) {}
// commas feel best for func style
native(C, CPP, OPENCL) {}
// and || feels best for preproc
native C || CPP {}
native C, CPP {}
Either one is fine, though func feels cleaner. I'd say your choice which one it'd be.
The question is, would language flags be predefined when compiling to that languahe or if it'd be up to user to define it with -D
flag?
Another question is if it'd be predefined would it be allowed to use user-defined flags (I vote for yes). For example if languages are defined and I want to use some generic code for, let's say, both C, C++ and OpenCL and I'd define CLIKE
flag, it'd be useful to be able to use it with native block instead of listing all languages (in some cases it could also contain Vala, D and other languages that have C interop).
native (C || CPP || OPENCL) {}
This looks best:
if
(yes, I'm aware of Raku, Go, Rust removing them)||
is clear, the comma is notI think fut should predefine the preprocessor symbol for the language it is transpiling to, to make it standard and easy-to-use.
We risk a potential collision with languages added in the future. Shouldn't be a big problem, though.
Possible mitigation is defining the language symbol as lowercase and enforcing -D
to be uppercase - but that looks odd and c
, d
could be confused with local variables.
Perhaps we should drop the condition-less native
syntax? I expect native
are always wrapped in #if
anyway.
As we're brainstorming here, in the beginning of Fusion, I also considered
#c printf("Hello, world!\n");
@java System.out.println(42);
The @
symbol is currently unassigned in the whole language. But native
doesn't seem so common to justify the use of this character.
What if
native (@C || @D) {}
Or #C
or some other symbol to distinguish languages from user-defined flags.
And tbh I don't think there's going to be any collision with languages, there's barely any with same names. I know, I've looked at a lot of them. I'd say I saw more then 200 and only collisions I saw were some unknown toy languages.
Also I'm personally not fan of ||
because it kind of implies possiblity of &&
(also since it's flags why not |
)... Actually, I am a fan now, what if to allow it? It's not going to work with languages, but possibly (@C | @CPP) & LINUX
or something like that.
Yea, maybe remove second bar and certainly allow having full on expressions!
And tbh I don't think there's going to be any collision with languages, there's barely any with same names.
I didn't mean different languages having the same name.
I meant that if someone uses fut -D FOO
for some conditional compilation, then suddenly a new language Foo becomes popular, and fut picks it as its target, this breaks #if FOO
. Now that I think of it, this requires that the user starts targeting the new language. So not a problem.
The @
prefixes are interesting, but I think we don't need the symbols for target languages look different from regular -D
symbols.
You could also add a warning/error when user flags are colliding with predefined ones so that it's more apparent
Current way of doing conditional compilation is nice, but it is very reminiscent of C's
#ifdef
s, which when used withnative
blocks is kind of cumbersome.From all languages I've seen D seems to handle conditional compilation the best. For checking compiler flags (like fut's
-D
) it usesversion
blocks, which are used in this way:Which lands itself nicely with chaining code blocks
My proposal is to instead of current conditional compilation blocks introduce something similar to D's
version
statement.Example:
And since it's code block based it lands nicely with both native blocks and out of box elseif support.
More about conditional compilation in D: https://dlang.org/spec/version.html (there's a bunch more interesting features)
Related: #142