I am confused about the as const cast. I checked a few documents and videos but did not understand it fully.
My concern is what does the as const mean in the code below and what is the benefit of using it?
const args = [8, 5] as const;
const angle = Math.atan2(...args);
console.log(angle);
This is known as a const assertion. A const assertion tells the compiler to infer the narrowest* or most specific type it can for an expression. If you leave it off, the compiler will use its default type inference behavior, which will possibly result in a wider or more general type.
Note that it is called an "assertion" and not a "cast". The term "cast" is generally to be avoided in TypeScript; when people say "cast" they often imply some sort of effect that can be observed at runtime, but TypeScript's type system, including type assertions and const assertions, is completely erased from the emitted JavaScript. So there is absolutely no difference at runtime between a program that uses as const and one that does not.
At compile time, though, there is a noticeable difference. Let's see what happens when you leave out as const in the above example:
const args = [8, 5];
// const args: number[]
const angle = Math.atan2(...args); // error! Expected 2 arguments, but got 0 or more.
console.log(angle);
The compiler sees const args = [8, 5]; and infers the type of number[]. That's a mutable array of zero or more elements of type number. The compiler has no idea how many or which elements there are. Such an inference is generally reasonable; often, array contents are meant to be modified in some way. If someone wants to write args.push(17) or args[0]++, they'll be happy with a type of number[].
Unfortunately the next line, Math.atan2(...args), results in an error. The Math.atan2() function requires exactly two numeric arguments. But all the compiler knows about args is that it's an array of numbers. It has completely forgotten that there are two elements, and so the compiler complains that you are calling Math.atan2() with "0 or more" arguments when it wants exactly two.
Compare that to the code with as const:
const args = [8, 5] as const;
// const args: readonly [8, 5]
const angle = Math.atan2(...args); // okay
console.log(angle);
Now the compiler infers that args is of type readonly [8, 5]... a readonly tuple whose values are exactly the numbers 8 and 5 in that order. Specifically, args.length is known to be exactly 2 by the compiler.
And this is enough for the next line with Math.atan2() to work. The compiler knows that Math.atan2(...args) is the same as Math.atan2(8, 5), which is a valid call.
And again: at runtime, there is no difference whatsoever. Both versions log 1.0121970114513341 to the console. But const assertions, like the rest of the static type system, are not meant to have effects at runtime. Instead, they let the compiler know more about the intent of the code, and can more accurately tell the difference between correct code and bugs.
Playground link to code
* This isn't strictly true for array and tuple types; a readonly array or tuple is technically wider than a mutable version. A mutable array is considered a subtype of a readonly array; the former is not known to have mutation methods like push() while the latter does.
In short words, it lets you create fully readonly objects, this is known as const assertion, at your code as const means that the array positions values are readonly, here's an example of how it works:
const args = [8, 5] as const;
args[0] = 3; // throws "Cannot assign to '0' because it is a read-only
args.push(3); // throws "Property 'push' does not exist on type 'readonly [8, 5]'"
You can see at the last thrown error, that args = [8, 5] as const is interpreted as args: readonly [8, 5], that's because the first declaration is equivalent to a readonly tuple.
Tuples have a lot of use cases, some examples are: spread parameters, destructive values, etc.. A general benefit is the readonly behaviour that is added to all its object attributes:
const args = [8, 5];
// Without `as const` assert; `args` stills a constant, but you can modify its attributes
args[0] = 3; // -- WORKS
args.push(3); // -- WORKS
// You are only prevented from assigning values directly to your variable
args = 7; // -- THROWS ERROR
There's a few exceptions for the asserts being 'fully readonly', you can check them here. For more details, here's a list of other related question/answers that helped me understand the const assertion:
How do I declare a read-only array tuple in TypeScript?
Any difference between type assertions and the newer as operator in TypeScript?
Typescript readonly tuples
If you were to write const args = [8, 5], nothing would prevent you from then also writing args[0] = 23 or args.push(30) or anything else to modify that array. All you've done is tell TS/JS that the variable named args points to that specific array, so you can't change what it's referencing (e.g. you can't do args = "something else"). You can modify the array, you just can't change what its variable is pointing to.
On the other hand, adding as const to a declaration now really makes it constant. The whole thing is read-only, so you can't modify the array at all.
To clarify, as pointed out in the comments:
"really makes it constant" could imply that there is some runtime effect when there is none. At runtime, args.push(30) will still modify the array. All as const does is make it so that the TypeScript compiler will complain if it sees you doing it. – jcalz
as const only affects the compiler, and there is an exception to its read-only effect (see the comments). But in general, that's still the major use difference between const and as const. One is used to make a reference immutable and the other is used to make what's being referenced immutable.
That is a const assertion. Here is a handy post on them, and here is the documentation.
When we construct new literal expressions with const assertions, we can signal to the language that
no literal types in that expression should be widened (e.g. no going from "hello" to string)
object literals get readonly properties
array literals become readonly tuples
With const args = [8, 5] as const;, the third bullet applies, and tsc will understand it to mean:
// Type: readonly [8, 5]
const args = [8, 5] as const;
// Ok
args[0];
args[1];
// Error: Tuple type 'readonly [8, 5]' of length '2' has no element at index '2'.
args[2];
Without the assertion:
// Type: number[]
const args = [8, 5];
// Ok
args[0];
args[1];
// Also Ok.
args[2];
as const when applied to an object or array it makes them immutable (i.e. making them read-only). For other literals it prevents type widening.
const args = [8, 5] as const;
args[0] = 10; ❌ Cannot assign to '0' because it is a read-only property.
Few other advantages :
You can catch bugs at compile-time without running the program if you don't cast to a different type.
The compiler will not allow you to reassign properties of nested objects.
I'm trying to code a type to represent a pointer in GPU device, and it should act like an array, with indexed property for get and set. I get no problem if the element type is a primitive type, but when I use a struct, I cannot change its member's value.
See this code:
#nowarn "9"
open System
open System.Runtime.InteropServices
[<Struct;StructLayout(LayoutKind.Sequential)>]
type MyStruct =
val mutable x : int
val mutable y : int
val mutable z : int
override this.ToString() = sprintf "(%d,%d,%d)" this.x this.y this.z
let deviceOnly() = failwith "this function should be used in quotation only"
type DevicePtr<'T>(h:nativeint) =
member this.Handle = h
member this.Item with get (idx:int) : 'T = deviceOnly() and set (idx:int) (value:'T) : unit = deviceOnly()
member this.Reinterpret<'T2>() = DevicePtr<'T2>(h)
member this.Volatile() = DevicePtr<'T>(h)
static member (+) (ptr:DevicePtr<'T>, offset:int) = DevicePtr<'T>(ptr.Handle + nativeint(offset * sizeof<'T>))
let test() =
let mutable test1 = MyStruct()
test1.x <- 1
let foo (collection:MyStruct[]) =
collection.[0].x <- 1
let bar (collection:DevicePtr<MyStruct>) =
collection.[0].x <- 1
//error FS0257:
// Invalid mutation of a constant expression.
// Consider copying the expression to a mutable local, e.g. 'let mutable x = ...'.
So, the type is DevicePtr<'T>, and it has indexed property Item with both get and set method. but the get method just return a value of 'T, so I cannot mutate it. But the system array works.
Anyone has some experience like this? to create a type works like array? which I hope the get function of the indexed property return a mutable ref instead of a value.
You cannot create a type that works with structures like an array because the language and runtime give special handling to arrays that other classes do not get.
With an array, an expression accessing an element that is a structure, as in your foo function, causes the element to be directly modified in place. The collection.[0] is effectively setting up a "pointer" or "reference" to an element that the .x expression is then applied to, allowing you to manipulate the object in place.
For other classes, the indexer is just another function, meaning a copy of the value is being returned. So in the bar function, the collection.[0] creates a copy of the value returned instead of the reference you get from the array. Because the .x would be modifying the temporary copy, the compiler issues the error you see. (A very similar warning would occur in C# or VB for similar code.) As the message suggests, you will need to create a variable to hold the copy, modify the copy, and assign it back to collection.[0].
I have tried:
const ascii = "abcdefghijklmnopqrstuvwxyz"
const letter_goodness []float32 = { .0817,.0149,.0278,.0425,.1270,.0223,.0202, .0609,.0697,.0015,.0077,.0402,.0241,.0675, .0751,.0193,.0009,.0599,.0633,.0906,.0276, .0098,.0236,.0015,.0197,.0007 }
const letter_goodness = { .0817,.0149,.0278,.0425,.1270,.0223,.0202, .0609,.0697,.0015,.0077,.0402,.0241,.0675, .0751,.0193,.0009,.0599,.0633,.0906,.0276, .0098,.0236,.0015,.0197,.0007 }
const letter_goodness = []float32 { .0817,.0149,.0278,.0425,.1270,.0223,.0202, .0609,.0697,.0015,.0077,.0402,.0241,.0675, .0751,.0193,.0009,.0599,.0633,.0906,.0276, .0098,.0236,.0015,.0197,.0007 }
The first declaration and initialization works fine, but the second, third and fourth don't work.
How can I declare and initialize a const array of floats?
An array isn't immutable by nature; you can't make it constant.
The nearest you can get is:
var letter_goodness = [...]float32 {.0817, .0149, .0278, .0425, .1270, .0223, .0202, .0609, .0697, .0015, .0077, .0402, .0241, .0675, .0751, .0193, .0009, .0599, .0633, .0906, .0276, .0098, .0236, .0015, .0197, .0007 }
Note the [...] instead of []: it ensures you get a (fixed size) array instead of a slice. So the values aren't fixed but the size is.
As pointed out by #jimt, the [...]T syntax is sugar for [123]T. It creates a fixed size array, but lets the compiler figure out how many elements are in it.
From Effective Go:
Constants in Go are just that—constant. They are created at compile time, even when defined as locals in functions, and can only be numbers, characters (runes), strings or booleans. Because of the compile-time restriction, the expressions that define them must be constant expressions, evaluatable by the compiler. For instance, 1<<3 is a constant expression, while math.Sin(math.Pi/4) is not because the function call to math.Sin needs to happen at run time.
Slices and arrays are always evaluated during runtime:
var TestSlice = []float32 {.03, .02}
var TestArray = [2]float32 {.03, .02}
var TestArray2 = [...]float32 {.03, .02}
[...] tells the compiler to figure out the length of the array itself. Slices wrap arrays and are easier to work with in most cases. Instead of using constants, just make the variables unaccessible to other packages by using a lower case first letter:
var ThisIsPublic = [2]float32 {.03, .02}
var thisIsPrivate = [2]float32 {.03, .02}
thisIsPrivate is available only in the package it is defined. If you need read access from outside, you can write a simple getter function (see Getters in golang).
There is no such thing as array constant in Go.
Quoting from the Go Language Specification: Constants:
There are boolean constants, rune constants, integer constants, floating-point constants, complex constants, and string constants. Rune, integer, floating-point, and complex constants are collectively called numeric constants.
A Constant expression (which is used to initialize a constant) may contain only constant operands and are evaluated at compile time.
The specification lists the different types of constants. Note that you can create and initialize constants with constant expressions of types having one of the allowed types as the underlying type. For example this is valid:
func main() {
type Myint int
const i1 Myint = 1
const i2 = Myint(2)
fmt.Printf("%T %v\n", i1, i1)
fmt.Printf("%T %v\n", i2, i2)
}
Output (try it on the Go Playground):
main.Myint 1
main.Myint 2
If you need an array, it can only be a variable, but not a constant.
I recommend this great blog article about constants: Constants
As others have mentioned, there is no official Go construct for this. The closest I can imagine would be a function that returns a slice. In this way, you can guarantee that no one will manipulate the elements of the original slice (as it is "hard-coded" into the array).
I have shortened your slice to make it...shorter...:
func GetLetterGoodness() []float32 {
return []float32 { .0817,.0149,.0278,.0425,.1270,.0223 }
}
In addition to #Paul's answer above, you can also do the following if you only need access to individual elements of the array (i.e. if you don't need to iterate on the array, get its length, or create slices out of it).
Instead of
var myArray [...]string{ /* ... */ }
you can do
func myConstArray(n int) string {
return [...]string{ /* ... */ }[n]
}
and then instead of extracting elements as
str := myArray[i]
you extract them as
str := myConstArray(i)
Link on Godbolt: https://godbolt.org/z/8hz7E45eW (note how in the assembly of main no copy of the array is done, and how the compiler is able to even extract the corresponding element if n is known at compile time - something that is not possible with normal non-const arrays).
If instead, you need to iterate on the array or create slices out of it, #Paul's answer is still the way to go¹ (even though it will likely have a significant runtime impact, as a copy of the array needs to be created every time the function is called).
This is unfortunately the closest thing to const arrays we can get until https://github.com/golang/go/issues/6386 is solved.
¹ Technically speaking you can also do it with the const array as described in my answer, but it's quite ugly and definitely not very efficient at runtime: https://go.dev/play/p/rQEWQhufGyK
How can I specifically define a new array of a specific type T ?
Strangely, I couldn't find any helpful information about it..
I want to write something like this:
let arr = new Array()
only that the elements of arr must be of type T.
How can I do it on F# ?
If type of elements is unknown, you can use explicit type annotation:
let arrayOfTenZeroes = Array.zeroCreate<int> 10
let emptyIntArray: int array = [||]
When you use high-order functions from Array module, you don't have to do so since type of elements is automatically inferred by the type checker:
// arr is inferred as int array
let arr = Array.init 10 (fun i -> i+1)
Maybe the Array.init<'T> and Array.create<'T> functions are what you are looking for.
Also consider using a Sequence instead of an array.
A sequence is a logical series of elements all of one type. Sequences are particularly useful when you have a large, ordered collection of data but do not necessarily expect to use all the elements.
Perhaps you can try something like this.
let myStringArray : string array = Array.zeroCreate 10
let myIntArray : int array = Array.zeroCreate 10
let myCharArray : char array = Array.zeroCreate 10
It's described on msdn.
Automatic generalization and type inference are great features you should take advantage of.
When an array is created:
let arr = Array.zeroCreate 10
its type is generalized. In this case it's inferred to be 'T[] (as general as possible).
Once you do something like:
let x = arr.[0] + 1
or
printfn "%s" arr.[0]
it can infer the concrete type (int[] or string[], respectively).
The lack of explicit types makes your code much cleaner. Save type annotations for when they're truly needed.