Why doesn't this work?
open System
let ary = Array.create<Int16> 10
ary.[0] <- 42 // compiler error
printfn "%d" ary.[0] // compiler error
The error I get is something like:
The operator 'expr.[idx]' has been used on an object of indeterminate type based on information prior to this program point. Consider adding further type constraints
The signature for Array.create<'T> is:
Array.create : int -> 'T -> 'T []
Currently you're only providing the first argument (number of elements to create) so ary is actually a function: Int16 -> Int16 []
You need to pass the second argument which is the value to use for the elements in the array:
let ary = Array.create<Int16> 10 0s
If you want the type's default value to be used for all the elements in the array (as it is in the above example) then you can use Array.zeroCreate as #Sehnsucht has pointed out
Related
What is the reason that, given a (variadic) function
func varargs(n ...int) {}
it can be called like
varargs(1, 2, 3, 4) // Fixed number of arguments
but not with an array:
a := [4]int{1, 2, 3, 4} // Fixed number of elements
varargs(a...) // Error: cannot use (type [4]int) as type []int in argument
I understand why
var s []int = a
wouldn't work: it prevents accidental misuse, requiring manual slicing:
s := a[:]
But why does this restriction extend to calls to variadic functions?
Bonus question:
Conversely, why would calling
func fourargs(w, x, y, z int) {}
with a 4-element array like
fourargs(a...) // Error: not enough arguments in call have ([4]int...)
// want (int, int, int, int)
also be forbidden?
It can be type-checked at compile time.
Spec: Passing arguments to ... parameters:
If the final argument is assignable to a slice type []T, it may be passed unchanged as the value for a ...T parameter if the argument is followed by .... In this case no new slice is created.
So when you have a slice and you pass it as the value of the variadic parameter, no new slice is created, it is just assigned.
If you have an array, that is a different type, that is not assignable to a slice type. Therefore it is not allowed.
You must first slice the array, which you can do without an intermediate variable:
a := [4]int{1, 2, 3, 4}
varargs(a[:]...)
Yes, you could say this automatic slicing could happen automatically / internally. Why this isn't allowed is opinion based (and belongs to the authors of Go). Basically, in Go arrays are secondary. Slices are the way to go. You should have a slice in the first place, which you can pass and you don't have a problem. See related questions: Why have arrays in Go? and Slicing a slice pointer passed as argument.
I have an array of type [String]
let names = ["Joffrey", "Cersei", "Mountain", "Hound"]
I have a function which takes an array of [Any] type.
func printItems(items: [Any]){
for item in items {
print(item)
}
}
Now when I call the function with names as parameters,
printItems(names)
I get an error Cannot invoke 'printItems' with an argument list of type '([String])'.
Any is just a typealias for a protocol which all types implicitly conform to.
Thoughts?
This is a surprising limitation of Swift. You can't cast an array to type [Any] so you can't pass it to a function taking type [Any]. You can use map to cast each item of the array:
printItems(names.map {$0 as Any})
But, the right way to do this in Swift is to use Generics:
func printItems<T>(items: [T]) {
for item in items {
print(item)
}
}
let names = ["Joffrey", "Cersei", "Mountain", "Hound"]
let numbers = [3.1416, 2.71818, 1.4142, 1.618034]
printItems(names) // This now works
printItems(numbers) // This works too
While every type conforms Any, this is not the same as it being a universal implicit superclass that all types inherit from.
When you cast a type to a protocol, you create a new value with a different structure. So for a string to be of type Any, it needs to be physically transformed from the String representation:
sizeof(String) // 24 bytes (on 64-bit, anyway)
to the Any representation:
sizeof(Any) // 32 bytes, includes some meta data
// about what the type really is
Since value types are held directly in the array, the array would be a very different shape so under the hood the compiler would have to do the equivalent of this:
names.map { $0 as Any } // create a new array, with the Any versions
Swift could perhaps automate this process for you (it does if you pass a single variable into a function that takes Any). But personally I’m glad it doesn’t, I’d rather this be more explicit – suppose your array was huge, this would be a lot of processing happening implicitly under the hood.
This is different from when you have an array of reference types, all of which are pointers to the actual data and so all the same size, and which need no transformation when upcasting:
class C { }
class D: C { }
let d = D()
let c: C = d
unsafeBitCast(d, UnsafePointer<Void>.self) // these value will
unsafeBitCast(c, UnsafePointer<Void>.self) // be the same
So saying “this array of [D] is really an array of [C]” is just a matter of the compiler agreeing the types can be substituted, no data transformation needs to take place:
// so this works fine,
// no runtime transformation needed:
func f(cs: [C]) { }
let ds = [D(),D()]
f(ds)
But protocols still are different from superclass references when used with classes:
protocol P { }
extension C: P { }
sizeofValue(C()) // 8 bytes (just a pointer)
sizeofValue(C() as P) // 40 bytes
func g(ps: [P]) { }
g(ds) // won’t compile, needs transformation
You need to explicitly put in your let declaration that you
are declaring an Any array and not a specific type array.
With your current declaration, Swift will evaluate it to [String] array.
and not as [Any] array.
To fix your problem just do the following:
let names : [Any] = ["Joffrey", "Cersei", "Mountain", "Hound"]
If you want to retain your let declaration, use AnyObject
in your printItems function and it will be accepted by Swift.
func printItems(items: [AnyObject]){
for item in items {
print(item)
}
}
I have a function that makes Arrays of specific type:
def mkArray[A:ClassTag:Ordering](size:Int):Array[A] = Array.ofDim[A](size)
And I want to make array arr of type Int or String depending on String str like so:
var arr = if(str=="i"){mkArray[Int](size)}else{mkArray[String](size)}
and now I try to add values to the array like so:
arr(n) = num.toInt // num is String like "123"
But it says:
- type mismatch; found : Int required: _366 where type _366 >: Int
with String
How can I get around this, making arr of type Array[Int] or Array[String] depending on string str?
Any help is appreciated,
Thanks!
Scala is a statically typed language, and in your case the type of arr is Array[_ >: Int with String]. Hence, if you give it an Int, you get a type error.
Depending on how you're using the array further in your code, I'd recommend you take a look at Either[1], since it may be helpful in keeping an Array[Either], and processing it using pattern matching differently for when it contains an Int vs String.
[1] http://danielwestheide.com/blog/2013/01/02/the-neophytes-guide-to-scala-part-7-the-either-type.html
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].
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.