-
Notifications
You must be signed in to change notification settings - Fork 21
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
suggest: valueof
operator to eval expression at compile time
#804
Comments
You can already do this, let name = "abc"
let length = name.Length // gives 3 And because it's a let binding, if it's part of a module, it's only calculated once. |
@abelbraaksma but |
Ah, you mean you want some way of writing expressions that evaluate to literals (aka constants), in other words, you want to introduce a macro language, something like C++ has? Maybe you can clarify that in your original text,and add use cases and more examples that are resolved (take a look at the template). It may also help to emphasize the constant nature, like: It could be powerful, but I wouldn't be surprised if there's already a suggestion hanging around with a similar idea. |
fsharp should be able to inference that some values are constants actually in logical. it will be reduced a lot of boilerplate design pattern. for example, memoize, curray ... there are lot of use cases, i can proivde a case. excel sheet have address of range represent ///excel sheet max column address
let maxLetters = "XFD" //letter maxColumn
/// [|x;d;f|] index of a..z
let maxValues = [|23; 5; 3|]
/// d + f * 26 + x * 26 * 26
let maxColumns = 16384 //position maxLetters
/// xfd.length = 3
let maxLength = 3
Debug.Assert((maxLength = maxLetters.Length), "maxLength", maxLetters.Length.ToString()) |
valueof
operator just like nameof
In F#, const variables are marked with Adding support for real |
@yatli it's no important, infer out 编译时推算全局不变量确实是一个艰巨的任务。能够推算一些简单的计算,比如 compile-time evaluation some |
> let (+) x y = printfn "Hi~"; x - y
let a = 1 + 1
let b = 3 + 2;;
Hi~
Hi~
val ( + ) : x:int -> y:int -> int
val a : int = 0
val b : int = 1
> let [<Literal>] a' = 1 + 1;;
let [<Literal>] a' = 1 + 1
---------------------^^^^^
stdin(13,22): error FS0267: This is not a valid constant expression or custom attribute value
|
I think @xp44mm would want something like this: [<ConstExpr>]
let ( + ) x y = x - y
[<Literal>]
let a = 1 + 1 In a constexpr function, all bindings need to be literal. As long as we keep |
What about |
Strings are a little bit special already because of the private implementations. A string is an object, has a ton of constructors, and yet it's not mutable as arrays/lists, and could be literals. |
#804 (comment) |
What do you mean? Arrays can only be |
If we want to support constexpr for any type, a potential way is to keep the initialization bytecode in the assembly -- so when another assembly references it, it can run the initializer again at compile time.. Sounds like, this can be done in a language neutral way, something like an IL injector. |
@xp44mm recommend to update the title to something more truly reflects the intent. :) |
One of the C# discussions on this topic: |
i just want to F# recongnize some let abc = "abc"
let chars= abc.ToCharArray()
let len = abc.Length the compiler will transpile it to as: let abc = "abc"
let chars= [|'a';'b';'c'|]
let len = 3 |
Isn't that the current state? The let binding is evaluated only once. You can put it in global scope to ensure that. |
@Happypig375 |
How will you know that |
@Happypig375 |
> let r = System.Random()
type System.String with
member _.Length' = r.Next()
let a = "123".Length'
let b = "123".Length';;
val r : System.Random
type String with
member Length' : int
val a : int = 20296113
val b : int = 36580259 abc is a literal |
@Happypig375 the property or function need is pure function. purity function that take arguments that are all constant return constant result. |
You cannot determine whether |
no effect itself, && no call impure function, is pure function. |
How can this be checked? |
first, provide some basic pure function as seed. |
Which? |
operator, arith +-*/, Math operator, relation operator, string operator, compare operator. maybe so. Excel function most in. |
Operators only? |
i think whilelist can very long. when you find some rule, then to shorten whilelist. |
Another approach is that, transitivity is only a constraint within a function (to check one declared |
amost all of Excel function is pure function. can in whilelist. |
@yatli |
Then the code in the first post will not work. Please update. |
the excel function random, now row, column, address, caller is effect function. just several. |
valueof
operator just like nameof
valueof
operator to eval expression at compile time
There's nothing in F# that should be compared to Excel. Excel is not even a programming language. The problem in this discussion is one of definition. I think the OP wants auto-magic constants, where a let binding 'transpiles' to a constant. But this can never, ever work, because a constant in the .NET world is replaced by its value when consumed. That means, if a.dll has a constant, and b.dll consumes this, and we now change the value in a.dll, it will not be reflected in b.dll until you recompile it. Therefore, magically changing existing let bindings into constants is a no-go. There is, however, something to say for constant evaluation of expressions, provided that the compiler can infer this. This isn't as hard as it looks and there's already a proposal somewhere for F#. We can just use
They are not. They can change state by virtue of the mapper or folder function you use. Besides, their output is never constant (remember that compile time constants can only ever be the built in value types that are not structs, and strings; not even a decimal can be costant, for instance). I agree that to pre-calculate one time evaluations can be valuable, but if they are expensive calculations, you should reconsider your design. If they aren't expensive, the benefit is minimal, as all given examples from the OP are already valid code. I like the idea of an expression that forces compile time evaluation, but by virtue of the limits of CLR, this will have a very limited set of allowed functions (and let's be fair, if you know beforehand that the length of a string in your code is constant for eternity, you can micro optimize yourself by replacing it with a constant, I think we need better use cases. Here's one: current time, which could be stored as ticks as int64). |
It took me a moment to find it, but the proposal for allowing expressions in literals already exists and is approved. It just needs someone to work on it: #539 It's not exactly the same as the OP asks, but it goes a long way and could in time be expanded. |
@abelbraaksma i think literal more than constant, literal include constant you metioned and also include tuple literal list literal, optional literal, any immutable data struct's literal. even any can be serialized data. for example array literal json literal. |
this do not matter. because the |
Actually, no. In F#, a literal, declared with the And constants, or literals, by their very definition, cannot be reference types (the exception being strings and byte arrays). So your example of regex won't work, not even if you allow calculations of function calls in literals that are to be evaluated at compile time. The only operators presently allowed are To get what you want with regexes, you can actually use the methods available from Wanting to allow a "constant" like that, i.e., a whole object, or in this case an actual assembly, to be pre-compiled and stored in your DLL or EXE would mean that somehow the compiler needs to serialize and deserialize the data that you you pre-computed. That's far from trivial, there are books written on that subject alone, and likely not going to happen (but I'm not the one who decides, if there's a reasonable proposal and it gets traction, and you're willing to implement it, they may accept it). An alternative you might consider is using a About regexes in particular, a good tool that I use is this Regular Expression Library Builder, which can do exactly what you suggest and create "constant", that is, DLL assemblies, of your compiled regular expressions. |
@abelbraaksma let len = 3 // abc.Length or i can test it: Assert.equal(len,abc.Length) maybe i can just keep it no changed: let len = abc.Length |
I'm not sure you understand how literals, or constants work. I don't understand what you mean with "infer" here. Let's say you have: // a.dll
let [<Literal>] SomeString = "John"
// b.dll
let [<Literal>] LengthSomeString = SomeString.Length
// c.dll
let [<Literal>] DoubleLength = LengthSomeString * 2 If one company created This can also happen with actual constants and is a well documented fact and cause of confusion for C# developers. Allowing arbitrary expressions is potentially going to wreak havoc. I still think there's value in an expression like |
I would think the function could only apply to things that you could also apply the |
@cartermp, I think it should be allowed everywhere where we allow literals to appear (like A large part of the discussion was about the confusion of what the OP wanted, which frankly, I'm still unsure about, but taken as a compile time evaluation of a valid F# expression that yields a .NET Framework allowed literal, I think it has merit. |
@abelbraaksma |
@abelbraaksma another use case: reflection is some immutable data, it represent source code, never changed except source code changed. it can be resolved in advance at compiler. |
Sorry, still not sure what you mean, as this is simply not possible given the current state of the art of compilers for the .NET platform. So either you mean something that I don't understand (examples, please), or I'm afraid you may not understand what the purpose, usages and limits of literals are, which is fine, but then I suggest you read the article I referenced earlier. The types there are the types any literal is limited to by the .NET Framework. So whatever your proposal, it has to be within those limits, or you would have to make your suggestion to Roslyn in stead.
Nope, reflection is not immutable. That's why people use reflection: they only know the type signatures at runtime. For instance because the versions of the methods they inspect can be different, or because they use pluggable libraries. You could do it once, serialize it ( |
hi! maybe this can spark your idea: |
Constant folding already happens (try 10 + 32), empty functions are optimized away, small functions are inlined, either by F# or by CLR. Is there a specific case where you don't see it happen and you would like it improved? I'm sure improvements are possible, but we'll need concrete use cases. |
When using literals, I am rather looking for some sort of guarantees by the compiler to not process just once and cache it, but embed the value when compiling. I think one approach to this would be marking functions as |
@Swoorup That currently happens if you mark something as I believe IL limits what's allowed as constants: primitives, string, guid, type, byte array. Of these, F# doesn't support Any set of functions that is going to be allowed in constant folding operations must be limited to taking and returning said types, cannot have side effects and cannot use currying. Anything else should probably go through a user defined precompilation step. This is just my take on this to keep it simple, though technically these limitations could be lifted to some extend. For one, it would be nice if struct DU and records were allowed here. |
Considering what C# allows: I was wrong, only strings are reference types that are allowed, plus the native primitives. Arrays, guids and Types are not (though they are allowed by the IL, after all, you can use them in attributes). |
@abelbraaksma That sounds similar to the approach rust takes with the exception of proc macros. |
I'm closing this, as arbitrary compile-time evaluation like in the OP isn't planned to be part of F# |
fsharp have added
nameof
operator in recent version. it is just a syntax sugar. but people like it. I propose we should be to do further more.for example:
maybe, we can do this:
this code will be translated into previous code at compile time. it will reduce people a lot of typos. and more readable code.
maybe, we can get an inference:
The text was updated successfully, but these errors were encountered: