How to define an extention method for a non-generic array? - arrays

I cannot figure out what is the correct syntax to define an extension method on a non-generic array. This question deals with generic arrays, but trying a similar construct for a byte array doesn't work.
I have tried a lot of options:
type Byte ``[]`` with
type byte ``[]`` with
type Byte array with
type byte array with
type array<Byte> with
type array<byte> with
type []<Byte> with
type []<byte> with
as well as all of these constructs wrapped in double backticks or parenthesis, but nothing works. I downloaded the language specification, but it only has a generic array example.

This is likely a quirk with Optional Type Extensions, which can get pretty funky when generics are involved. I'd use an extension method like this instead:
open System.Runtime.CompilerServices
[<Extension>]
type ByteArrayExtensions =
[<Extension>]
static member inline Sum(xs: byte[]) = Array.sum xs
let f() =
let xs = [| byte(1); byte(2); byte(3) |]
xs.Sum()

Related

Why can't Kotlin implicitly upcast Array of String into Array of Any?

The following code implicitly upcasts a String into Any.
val s = "some string"
val upcasted: Any = s
However, the following does not compile (type mismatch):
val s = arrayOf("some string")
val upcasted: Array<Any> = s
You can successfully cast to Array<Any> as shown:
val s = arrayOf("some string")
val upcasted: Array<Any> = s as Array<Any>
However, this gives the warning "Unchecked cast: Array<String> to Array<Any>".
The same logic does appear to work with Lists, so is the internal implementation of Array incompatible with this type of casting? Perhaps it because of the in-memory representation of Arrays?
You can't safely cast a type to a supertype or subtype like this, because an Array<String> does not qualify as an Array<Any>. If you tried to put a Double in your supposed Array<Any>, it would throw an exception, because the actual type is a String array. Arrays are a special case of generics that don't have type erasure, so they are locked to the type they were instantiated with.
What you could do is cast it to an Array<out Any>, because you can safely use it that way. You can pull Strings out of the array and they qualify as instances of Any. The reverse is not true. You can't put any instance of Any and its subclasses into a String array.
When it comes to List, you can cast it to a List with the supertype type and you don't even have to manually cast it. It's safe so the cast can be done implicitly.
val s = listOf("some string")
val upcasted: List<Any> = s // implicit cast
So why don't you have to cast to List<out Any>? The List interface doesn't have any functions that allow you to add stuff to it. It is defined with an out-projected type in its declaration, so when you type List<Any> it is already the same thing as a List<out Any>
If you try to do this with a MutableList, which accepts putting items into it and is not defined with an out-projected type, then you will run into the same warning as you did with the array.
val s = mutableListOf("some string")
val upcasted: MutableList<Any> = s as MutableList<Any> // warning here and implicit cast impossible
The difference between this and the Array is that there is type-erasure, so you won't have a runtime exception if you try to add some non-String to this list. But there is the possibility of hidden bugs if you are not careful, hence the warning.

Declaring array with specific type but arbitrary number of dimensions

I am curious if there is any way in Swift to declare a multi-dimensional array that has a specific type(for this example we'll say Float) but isn't confined to a fixed number of dimensions(E.g., let arr: Array<Array<Float>>)?
I'd like to create a function that accepts a multi-dimensional array and currently the only way I'm aware of is:
func testFunction(arr: [Any]) {}
This is problematic though as it doesn't specify the actual scalar type of the array. As the type Any implies, this function can accept anything that is stuck inside of an Array. Any ideas?
Not with just an Array. Each array needs to know what type it contains. To contain an arbitrary number of dimensions, each array would need to be able to hold either a Float or an Array.
You could possible do something custom with an enum that has two cases.
enum MultidimensionalArray<Element> {
case array([MultidimensionalArray<Element>])
case values([Element])
}
but you'll probably have add a bunch more methods to it based on how you want to use ie.
How about generics
func testFunction<T>(arr: [T]) { }
T is a specific type and can be anything even
let foo = [[[[Float]]]]()
testFunction(arr: foo)
I found a way that makes it pretty simple to do what I'm after with some generic constraints and extensions.
protocol TypedNDArrayProtocol {}
// Make the type you are trying enforce conform to our protocol
extension Float: TypedNDArrayProtocol {}
// Now make Array conform to the same protocol while constraining
// it to only elements that *also* conform to this protocol
extension Array: TypedNDArrayProtocol where Element: TypedNDArrayProtocol {}
func testFunction(arr: Array<TypedNDArrayProtocol>) {}
This creates a sort of recursive conformance where the Array is allowed to accept either another Array, or a Float. If it is an Array, that Array can again only accept another Array or a Float and so on. This way you can use an arbitrary number of dimensions, but it must eventually terminate with a specific type; in this case a Float.
// This will work
testFunction(arr: [1.0, 2.0])
// This will also work
testFunction(arr: [[[3.0], [4.0]], [[5.0], [6.0]]])
// This will NOT work
testFunction(arr: [[[3], [4]], [[5], [6]]])
// Neither will this
testFunction(arr: [[["3"], ["4"]], [["5"], ["6"]]])

Array of Arrays as Iterable of Iterables

Let's consider the following definition to add up all elements in a nested Iterable structure of Ints:
def add(xss : Iterable[Iterable[Int]]) : Int = xss.map(_.sum).sum
However, evaluating following expression yields a type error:
scala> add(Array(Array(1,2,3)))
<console>:9: error: type mismatch;
found : Array[Array[Int]]
required: Iterable[Iterable[Int]]
add(Array(Array(1,2,3)))
^
The function works as expected with other Iterables (like Lists). How can I avoid that error? What's the rationale for it? Guess that is something to do with Arrays being native from Java, but don't know the specifics details in this case.
Thanks
It doesn't work because Scala would need to use 2 implicit conversion in a row to go from Array[Array[Int]] to Iterable[Iterable[Int]], which it (purposefully) doesn't do.
You could specify the outer Array's type :
scala> add(Array[Iterable[Int]](Array(1,2,3)))
res4: Int = 6
or transform its elements to Iterable[Int] (thus bypassing an implicit conversion) :
scala> add(Array(Array(1,2,3)).map(_.toIterable))
res5: Int = 6
The problem comes from the fact that Scala's Array[T] is just a representation for Java's T[]. What makes Array[T] behave like an usual Scala collection is an implicit conversion in Predef.
From Array's documentation :
Two implicit conversions exist in scala.Predef that are frequently
applied to arrays: a conversion to mutable.ArrayOps
and a conversion to mutable.WrappedArray (a subtype of scala.collection.Seq). Both types make available many of the standard
operations found in the Scala collections API. The conversion to
ArrayOps is temporary, as all operations defined on ArrayOps return an
Array, while the conversion to WrappedArray is permanent as all
operations return a WrappedArray.
The conversion to ArrayOps takes priority over the conversion to
WrappedArray.
Your intuition is correct. See the Array type signature:
final class Array[T] extends java.io.Serializable with java.lang.Cloneable
contrast that with Seq type signature:
trait Seq[+A] extends Iterable[A] with collection.Seq[A] ...
As you can see Array does not relate to the Iterable[A] trait.
You can fix this by calling toIterable on the instance:
scala> add(Array(Array(1,2,3).toIterable).toIterable)
res1: Int = 6

Scala generic arrays instantiating

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

defining a new array of a specific type on F#

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.

Resources