I have a generic class, which is constructed from multi-dimensional data (i.e. n-dimensional Ararys or Vectors).
In this case, I would like the class to be instantiated by one type only (e.g. Vector), aside from its dimensionality (Vector[Vector[T]] but not Vector[Array[T]]).
Having this class signature:
class Foo[T](x: Vector[T], y: Bar[T])
how could I guarantee that T would be for example Vector[T] or Vector[Vector[T]] (or etc...) but NOT Array[T] or Vector[Array[T]]?
While generics are wiped out at compile time by type-erasure, you can rely on the "evidence" mechanism.
An evidence is a special typeclass whose goal is only to witness the relation between type A and type B.
A<:<B is a witness that A is a subclass of B
A=:=B is a witness that A is a B
A>:>B is a witness that A is a superclass of B
so you can write something like that
case class MyContainer[A,B](b:Vector[B])(implicit ev: B <:< Vector[A])
Inside your class, you can simply treat every element of vector b as a Vector A by applying the evidence to each item: i.e.
b flatMap {
x => ev(x) map {_.toString}
}
Related
I spent some time last year trying to learn fp-ts. I've finally come around to using it in a project and a lot of my sample code has broken due to the recent refactoring. I've fixed a few of the breakages but am strugging with the others. It highlights a massive whole in my FP knowledge no doubt!
I had this:
import { strict as assert } from 'assert';
import { array } from 'fp-ts/Array';
import { getFoldableComposition, } from 'fp-ts/Foldable';
import { Monoid as MonoidString } from 'fp-ts/string'
import { none,some, option } from 'fp-ts/Option';
const F = getFoldableComposition(array, option)
assert.strictEqual(F.reduce([some('a'), none, some('c')], '', MonoidString.concat), 'ac')
getFoldableComposition, option and array are now deprecated. The comments on getFoldableComposition say to use reduce, foldMap or reduceRight instead, so, amongst other things, I tried this.
import { strict as assert } from 'assert';
import { reduceRight } from 'fp-ts/Foldable';
import { Monoid as MonoidString } from 'fp-ts/string'
import { some } from 'fp-ts/Option';
assert.strictEqual(reduceRight([some('a'), none, some('c')], '', MonoidString.concat), 'ac')
That's not even compiling, so obviously I'm way off base.
Could someone please show me the correct way to replace getFoldableComposition and, while we're at it, explain what is meant by 'Use small, specific instances instead' as well for option and array? Also, anything else I'm obviously doing wrong?
Thank you!
Let's start with your question
what is meant by 'Use small, specific instances instead' as well for option and array?
Prior to fp-ts v2.10.0, type class instances were grouped together as a single record implementing the interfaces of multiple classes, and the type class record was named after the data type for which the classes were defined. So for the Array module, array was exported containing all the instances; it had map for Functor and ap for Apply etc. For Option, the option record was exported with all the instances. And so on.
Many functions, like getFoldableComposition and sequenceT are defined very generically using "higher-kinded types" and require you to pass in the type class instance for the data type you wanted the function to use. So, e.g., sequenceT requires you to pass an Apply instance like
assert.deepEqual(
sequenceT(O.option)([O.some(1), O.none]),
O.none
)
Requiring these big records of type classes instances to be passed around like that ended up making fp-ts not tree-shake well in application and library code, because JS bundlers couldn't statically tell which members of the type class record where being accessed and which weren't, so it ended up including all of them even if only one was used. That increases bundle size, which ultimately makes your app load slower for users and/or increases the bundle size of libraries consuming your library.
The solution to this problem was to break the big type class records apart and give each type class its own record. So now each data type module exports small, individual type class instances and eventually the mega-instance record will be removed. So now you would use sequenceT like
assert.deepEqual(
sequenceT(O.Apply)([O.some(1), O.none]),
O.none
)
Now the bundler knows that only Apply methods are being used, and it can remove unused instances from the bundle.
So the upshot of all this is to just not use the mega instance record anymore and only use the smaller instance records.
Now for your code.
The first thing I'll say is talk to the compiler. Your code should give you a compile error. What I'm seeing is this:
So you passed reduceRight too many arguments, so let's look at the signature:
export declare function reduceRight<F extends URIS, G extends URIS>(
F: Foldable1<F>,
G: Foldable1<G>
): <B, A>(b: B, f: (a: A, b: B) => B) => (fga: Kind<F, Kind<G, A>>) => B
First thing you should note, this function is curried and requires three invocations in order to fully evaluate (i.e. it is curried to three separate function calls). First it takes the type class instances, then the accumulator and reducing function, and finally it takes the data type we are reducing.
So first it takes a Foldable instance for a type of kind Type -> Type, and another Foldable instance for another (or the same) type of kind Type -> Type. This is where the small vs big instance record comes into play. You'll pass SomeDataType.Foldable instead of SomeDataType.someDataType.
Then it takes polymorphic type B of kind Type as the initial value for the reduce (aka the "accumulator") and a binary function which takes polymorphic type A of kind Type and B and returns B. This is the typical signature of a reduceRight.
Then it takes a scary looking type which is making use of higher-kinded types. I would pronounce it as "F of G of A" or F<G<A>>. And finally it returns B, the reduced value.
Sounds complicated, but hopefully after this it won't seem so bad.
From looking at your code, it appears you want to reduce an Array<Option<string>> into a string. Array<Option<string>> is the higher-kinded type you want to specify. You just replace "F of G of A" with "Array of Option of string". So in the signature of reduceRight, F is the Foldable instance for Array and G is the Foldable instance for Option.
If we pass those instances, we'll get back a reduceRight function specialized for an array of options.
import * as A from 'fp-ts/Array'
import * as O from 'fp-ts/Option'
import { reduceRight } from 'fp-ts/Foldable'
const reduceRightArrayOption: <B, A>(
b: B,
f: (a: A, b: B) => B) => (fga: Array<O.Option<A>>) => B =
reduceRight(A.Foldable, O.Foldable)
Then we call this reduce with the initial accumulator and a reducing function that takes the value inside Array<Option<?>> which is string and the type of the accumulator, which is also string. In your initial code, you were using concat for string. That will work here, and you'll find it on the Monoid<string> instance in the string module.
import * as A from 'fp-ts/Array'
import * as O from 'fp-ts/Option'
import { reduceRight } from 'fp-ts/Foldable'
import * as string from 'fp-ts/string'
const reduceRightArrayOption: <B, A>(
b: B,
f: (a: A, b: B) => B) => (fga: Array<O.Option<A>>) => B
= reduceRight(A.Foldable, O.Foldable)
const reduceRightArrayOptionStringToString: (fga: Array<O.Option<string>>) => string
= reduceRightArrayOption("", string.Monoid.concat)
Finally, it's ready to take our Array<O.Option<string>>.
import * as assert from 'assert'
import * as A from 'fp-ts/Array'
import * as O from 'fp-ts/Option'
import { reduceRight } from 'fp-ts/Foldable'
import * as string from 'fp-ts/string'
const reduceRightArrayOption: <B, A>(
b: B,
f: (a: A, b: B) => B) => (fga: Array<O.Option<A>>) => B
= reduceRight(A.Foldable, O.Foldable)
const reduceRightArrayOptionStringToString: (fga: Array<O.Option<string>>) => string
= reduceRightArrayOption("", string.Monoid.concat)
const result = reduceRightArrayOptionStringToString([
O.some('a'),
O.none,
O.some('c'),
])
assert.strictEqual(result, "ac")
To simplify all of this, we can use the more idiomatic pipe approach to calling reduceRight:
import * as assert from "assert"
import { reduceRight } from "fp-ts/Foldable"
import * as string from "fp-ts/string"
import * as O from "fp-ts/Option"
import * as A from "fp-ts/Array"
import { pipe } from "fp-ts/lib/function"
assert.strictEqual(
pipe(
[O.some("a"), O.none, O.some("c")],
reduceRight(A.Foldable, O.Foldable)(string.empty, string.Monoid.concat)
),
"ac"
)
I know that was a lot, but hopefully it provides a little clarity about what's going on. reduceRight is very generic, in a way that almost no other TypeScript libraries attempt to be, so it's totally normal if it takes you a while to get your head around it. Higher-kinded types are not a built-in feature of TypeScript, and the way fp-ts does it is admittedly a bit of a hack to work around the limitations of TS. But keep playing around and experimenting. It'll all start to click eventually.
I've been having a lot of trouble making an array that is accessible to all the functions in my class in C++/CLI. Since it's C++/CLI, std::vector doesn't work. Boost::array throws an error about unmanaged types being used with managed types. I don't want to use a pointer array because I want to get the size of the array. System::List is too slow (every ms matters in my program, but if it helps, I don't care about write speeds to the array, only read speeds of one element at a time). cliext::vector was the closest I got to getting this to work.
I created a single dimensional cliext::vector with an int, and it worked. However, when I tried to use
cliext::vector<cliext::vector<int>> test;
it failed with a similar error as the one below. Here is how I used it in my class:
The header:
cliext::vector<Color> test;
I set values for it in the constructor:
test = gcnew cliext::vector<Color>(5);
test[0] = Color(255,255,255);
I then tried to make a class that would store 3 variable for color. Here is the header file. The constructor just sets the r,g,b values:
namespace FrameCalculator {
class Color {
public:
Color(int r, int g, int b);
int r;
int g;
int b;
};
However this didn't work, and it threw the error below:
1>E:\Microsoft Visual
Studio\2017\Community\VC\Tools\MSVC\14.11.25503\include\cliext\vector(1091):
note: see reference to class template instantiation
'cliext::impl::vector_base<_Value_t,false>' being compiled
1> with
1> [
1> _Value_t=FrameCalculator::Color
1> ]`
and
1>E:\Microsoft Visual
Studio\2017\Community\VC\Tools\MSVC\14.11.25503\include\cliext\vector(615):
error C3671: 'cliext::impl::vector_impl<_Value_t,false>::SyncRoot::get':
function does not override 'System::Collections::ICollection::SyncRoot::get'
1> with
1> [
1> _Value_t=FrameCalculator::Color
1> ]`
There were about 5 of each of these errors. What am I doing wrong? How do I get a 2d array that's not slow, is globally accessible. I don't need both arrays to be dynamic (I'd prefer them not to). I know the inner array will have 3 elements, but I won't know the outer array size at compile time. How do I achieve this?
You can not create a vector of .Net class object. You need a vector of handle types.
test = gcnew cliext::vector<Color^>(5);
Also your class objects must be of type ref class
ref class Color
Assignment is done with always creating a new object
test[0] = gcnew Color(255,255,255);
You can also use completely unmanaged types, but than you are forced to use pointers to this data in your C++/CLI code.
Managed types can never own unmanaged types. They may own a pointer to an unmanaged type.
So you may get all you anmanaged data into one unmanaged class and this data is accessed from the managed code.
there are two situation make me confuse when develop swift 2.2 by using Xcode 7.1, please see the example below, thanks
First, when import Foundation, I declared an testArray which contains two item, an Integer type 1 and a String type "hello", my question is why Swift type inference testArray to Array(NSObject) instead of Array(Any)
import Foundation
let testArray = [1, "hello"]
print(testArray.dynamicType) //testArray is Array<NSObject>
Second, when i remove import Foundation, the code below can't be compile, the error message is "Type of expression is ambiguous without more content", my question is why Swift not type inference to Array(Any) in this situation, thanks for help
let testArray2 = [2, "world"]
print(testArray2)
//can't compile, error message = "Type of expression is ambiguous without more content"
/// The protocol to which all types implicitly conform.
public typealias Any = protocol<>
Any is just a protocol that all types implicitly conform to – it's not a concrete type itself. Swift cannot infer an array of non-concrete types, which is why it fails to infer Any, but succeeds with NSObject (Int can be bridged to NSNumber, String can be bridged to NSString – and they both inherit from NSObject, which is a concrete type).
For example, consider this:
protocol Foo {}
struct Bar:Foo {}
struct Baz:Foo {}
let arr = [Bar(), Baz()] // error: Type of expression is ambiguous without more context
Because Foo is a non-concrete type, Swift cannot infer an array of it. You have to explicitly tell the compiler what you want its type to be:
let arr:[Foo] = [Bar(), Baz()]
You'll also get the same behaviour with AnyObject (as it's a protocol that all classes implicitly conform to – but still not a concrete type):
class Qux {}
class Fox {}
let a = [Qux(), Fox()] // error: Type of expression is ambiguous without more context
let a1:[AnyObject] = [Qux(), Fox()] // no error
Why Swift is unable to infer an array of non-concrete types is most likely due to the existing limitations of non-concrete types in the language – currently concrete types are required for most non-trivial operations. See this great Q&A for an example.
But to be honest, you should really be thinking more about whether you actually need an array of Any. I cannot think of a single practical application of having an array of Any, as because everything implicitly conforms to the elements, they must be guaranteed to do nothing (you can't call a specific method on something that could be anything). Sure you can type-cast, but what's the point in getting back the type safety that you threw away to begin with?
You should always be as type specific as you can. You could build a wrapper for your values – this could either be a simple struct to wrap a couple of properties, or a type erasure in order to wrap non-concrete types in a pseudo concrete type. At the very least, you should consider creating your own protocol that your array elements conform to.
Because it won't auto recognize array of Any
it will work if you define it as
let testArray2 :[Any] = [2, "world"]
the Foundation library imports the NS API, which automatically converts the 2 to NSNumberand "world" to NSString, converting it automatically to array of NSObject
In my Kotlin Android project, I made a FileItem class which extends Serializable
class FileItem(<parameters>) : Serializable, Comparable<FileItem> {
So I needed to Serialize instances of this class into a Bundle
val arguments:Bundle = Bundle()
arguments.putSerializable("folders", folders as Serializable)
where folders has been declared as :
folders:Array<FileItem> (method parameter)
The serialization code above compile without any warning. Meanwhile, the problem comes when I need to unserialize folders items :
val arguments: Bundle? = getArguments()
if (arguments != null){
foldersItems = arguments.getSerializable("folders") as Array<FileItem>
where foldersItems is declared as
var foldersItems: Array<FileItem>?
I get the following warning, that I can't manage to solve without suppress_warning annotation :
w: <Path to my class>: (78, 28): Unchecked cast: java.io.Serializable! to kotlin.Array<com.loloof64.android.chess_positions_archiver.main_file_explorer.FileItem>
This kind of code compiles in Java/Groovy without warning (folderItems is then a FileItem[]), so how can I modify the kotlin code for the compiler to be "satisfied" ?
I noticed in official Kotlin documentation that Kotlin Array does not extend Serializable and is not open for inheritance. Is it possible meanwhite to "add" it via a kind of extension method ?
In fact, the cast is not unchecked, the compiler's warning is misleading.
This happens because in Kotlin arrays are represented by generic class Array<T>, and the compiler treats it as usual generic class with type parameters erased at runtime.
But on JVM arrays have reified types, and when you cast something as Array<SomeType>, the generated bytecode really checks the type parameter to be SomeType as well as something being an Array<*>, which would only happen for any other generic class.
This example shows that the array cast is checked:
val a: Any = Array<Int>(1) { 0 }
val i = a as Array<Int>
val d = a as Array<Double> // gets checked and throws ClassCastException
The easiest solution is indeed to #Suppress("UNCHECKED_CAST"), because actually there should not be any warning.
I filed an issue describing the problem in Kotlin issue tracker.
The cast here is unchecked because the compiler here can't ensure the nullability of array's generic type parameter.
Consider the following example:
fun castAsArrayOfString(param: Any) = param as Array<String>
castAsArrayOfString(arrayOf("a")) // is Array<String>, all ok
castAsArrayOfString(arrayOf("a", null)) // is Array<String>, but contains null
So the compiler warns you about potential type safety problems this cast could introduce.
It's a follow up question on Flink Scala API "not enough arguments".
I'd like to be able to pass Flink's DataSets around and do something with it, but the parameters to the dataset are generic.
Here's the problem I have now:
import org.apache.flink.api.scala.ExecutionEnvironment
import org.apache.flink.api.scala._
import scala.reflect.ClassTag
object TestFlink {
def main(args: Array[String]) {
val env = ExecutionEnvironment.getExecutionEnvironment
val text = env.fromElements(
"Who's there?",
"I think I hear them. Stand, ho! Who's there?")
val split = text.flatMap { _.toLowerCase.split("\\W+") filter { _.nonEmpty } }
id(split).print()
env.execute()
}
def id[K: ClassTag](ds: DataSet[K]): DataSet[K] = ds.map(r => r)
}
I have this error for ds.map(r => r):
Multiple markers at this line
- not enough arguments for method map: (implicit evidence$256: org.apache.flink.api.common.typeinfo.TypeInformation[K], implicit
evidence$257: scala.reflect.ClassTag[K])org.apache.flink.api.scala.DataSet[K]. Unspecified value parameters evidence$256, evidence$257.
- not enough arguments for method map: (implicit evidence$4: org.apache.flink.api.common.typeinfo.TypeInformation[K], implicit evidence
$5: scala.reflect.ClassTag[K])org.apache.flink.api.scala.DataSet[K]. Unspecified value parameters evidence$4, evidence$5.
- could not find implicit value for evidence parameter of type org.apache.flink.api.common.typeinfo.TypeInformation[K]
Of course, the id function here is just an example, and I'd like to be able to do something more complex with it.
How it can be solved?
you also need to have TypeInformation as a context bound on the K parameter, so:
def id[K: ClassTag: TypeInformation](ds: DataSet[K]): DataSet[K] = ds.map(r => r)
The reason is, that Flink analyses the types that you use in your program and creates a TypeInformation instance for each type you use. If you want to create generic operations then you need to make sure a TypeInformation of that type is available by adding a context bound. This way, the Scala compiler will make sure an instance is available at the call site of the generic function.