Consider a type hierarchy, where the base object is non-generic, but sub-types are:
type
TestBase = ref object of RootObj
DerivedA = ref object of TestBase
DerivedB[T] = ref object of TestBase
field: T
proc testProc(x: TestBase) =
if x of DerivedB: # <= what to do here
echo "it's a B!"
else:
echo "not a B"
Using the of operator like this won't compile, because it expects object types. What does work is e.g. to match for specific types like DerivedB[int], or making the proc itself generic in T, which is meaningless when passing in a DerivedA.
Is there a way to solve this problem generically without resorting to methods and dynamic dispatch?
The easiest solution here is to introduce a dummy base type for all of the generic derived types, with the only purpose of assisting in such checks. Here is an example:
type
TestBase = ref object of RootObj
DerivedA = ref object of TestBase
# I'm using illustrative names here
# You can choose something more sensible
DerivedDetector = ref object of TestBase
DerivedB[T] = ref object of DerivedDetector
field: T
proc testProc(x: TestBase) =
if x of DerivedDetector: # This will be true for all derived types
echo "it's a B!"
else:
echo "not a B"
testProc DerivedB[int](field: 10)
testProc DerivedA()
This solution won't increase the size of the objects and it doesn't introduce any run-time overhead in typical code.
If you can't modify the inheritance hierarchy (it may be part of a third-party library), there is a much more complicated solution based on the getTypeInfo proc from the system module. This proc returns an opaque pointer that can be used as an identifier for the type. You'll have to register all derived types with their identifiers in a hash table (you can do this at the start of the program) and then use that to make run-time checks against the type-info pointers of the input values in the proc.
Related
I want to define my own type T for a MutableMapping[str, str]. AFAIK it can be done y type aliasing, or via NewType
from typing import MutableMapping, NewType
T_alias = MutableMapping[str, str]
T_new = NewType("T_new", MutableMapping[str, str])
The problem is the required syntax to initialize one variable of each kind. The correct initialization (which does not trigger any error in Pylance/Pyright) is:
vn: T_new
va: T_alias
vn = T_new({})
va = {}
The assignment of vn looks ok to me (although I find a bit awkward to have to pass the empty dict {}). However I don't like the assignment of va because it does not make clear that va is of type T_alias.
I tried:
va = T_alias({})
but it does not work (Expected no arguments to "MutableMapping" constructor). So I tried also:
va = T_alias()
but it did not work either. Although Pylance does not trigger any error (which may be considered a Pylance bug?), an exception happens at runtime: TypeError: Can't instantiate abstract class MutableMapping with abstract methods __delitem__, __getitem__, __iter__, __len__, __setitem__
So the only way to intialize va making explicit that it is of type T_alias is using typing.cast:
va = cast(T_alias, {})
which I find awkward too.
So the question is: am I undestanding correctly the use of type aliases and NewType? And also, which one would be advised for this particular case? I find the initialization of T_new objects cleaner (I don't like the cast()) but, I'm worried that the NewType() would introduce some overhead when dealing with these objects, and I don't really need to sublcass the dict, so I would prefer to use the alias.
I have a strategies expressed as generics in nim:
proc fooStrategy[T](t: T, ...)
proc barStrategy[T](t: T, ...)
I would like to create a lookup table for the strategies by name... so I tried:
type
Strategy*[T] = proc[T](t: T, ...)
let strategies* = toTable[string, Strategy[T]]([
("foo", fooStrategy), ("bar", barStrategy)])
This doesn't work -- the type declaration fails. If I were to get by that I could guess that the table of strategies would also have problems. Is there another way to do this? "T" is supposed to be "some 1D collection type" -- could be sequence, array, vector from blas, etc. I could add concrete strategies to the table for common collections, but I still have the problem with the function pointer, as
type
Strategy* = proc(t: any, ...)
let strategies* = toTable[string, Strategy]([
("foo-seq[int]", fooStrategy[int]), ...])
still has problems. Any suggestions?
There are multiple issues with your code:
Firstly, initTable does not take a list of items for the table. It only takes an initial size. You want to use toTable instead.
Secondly, you must explicitly set a value for the generic parameter T when creating a table, because at runtime, all generic parameters must be bound to a type.
Thirdly, the proc types have to exactly match, including pragmas on the proc. This one's tricky.
Here is a working example:
import tables
type
Strategy*[T] = proc(t: T) {.gcsafe, locks: 0.}
proc fooStrategy[T](t: T) = echo "foo"
proc barStrategy[T](t: T) = echo "bar"
let strategies* = toTable[string, Strategy[int]]([
("foo", fooStrategy[int]), ("bar", barStrategy[int])
])
For this example, I create a table with Strategy[int] values (you cannot have a table with Strategy[T] values as this is not a concrete type). I instantiate both fooStrategy and barStrategy with [int] to match the table type. I added {.gcsafe, locks: 0.} to the type definition. If this is omitted, you will get a compiler error:
test.nim(9, 49) Error: type mismatch: got (Array constructor[0..1, (string, proc (t: int){.gcsafe, locks: 0.})])
but expected one of:
proc (pairs: openarray[(string, Strategy[system.int])]): Table[system.string, Strategy[system.int]]{.gcsafe, locks: 0.}
As you see, the compiler tells you in the first line what it sees and in the third line what it expects. it sees procs with {.gcsafe, locks: 0.} because those pragmas are implicitly assigned to the procs defined above. The pragmas change the type, so to be able to assign those procs to Strategy[T], you have to define the same pragmas to Strategy[T].
There are various libraries in nim that return actual objects, rather than references. Sometimes I want an object on the heap (regardless of efficiency) -- for instance when I have a generic procedure that expects a ref to an object.
The only way to construct an IntSet on the heap I have found is:
proc newIntSet() : ref IntSet =
new(result)
assign(result[], initIntSet())
This seems to work, but feels like a hack. I worry if it only seems to work. (Are the structures copied by "assign" properly cleaned up?) Is there a better way? Is there a more generic way that will work with other objects?
Your code is perfectly valid. The resulting reference will be subject to garbage collection as any other referefence.
If you find yourself doing this often, you can define the following makeRef template to get rid of the code repetition:
template makeRef(initExp: typed): expr =
var heapValue = new(type(initExp))
heapValue[] = initExp
heapValue
Here is an example usage:
import typetraits
type Foo = object
str: string
proc createFoo(s: string): Foo =
result.str = s
let x = makeRef createFoo("bar")
let y = makeRef Foo(str: "baz")
echo "x: ", x.type.name, " with x.str = ", x.str
Which will output:
x: ref Foo with x.str = bar
I have a dictionary with Structs in it. I am trying to assign the values of the struct when I loop through the dictionary. Swift is telling me cannot assign to 'isRunning' in 'blockStatus'. I haven't been able to find anything in the docs on this particular immutability of dictionaries or structs. Straight from the playground:
import Cocoa
struct BlockStatus{
var isRunning = false
var timeGapForNextRun = UInt32(0)
var currentInterval = UInt32(0)
}
var statuses = ["block1":BlockStatus(),"block2":BlockStatus()]
for (block, blockStatus) in statuses{
blockStatus.isRunning = true
}
cannot assign to 'isRunning' in 'blockStatus'
blockStatus.isRunning = true
This does work if I change the struct to a class.
I am guessing it has something to do with the fact that structs are copied and classes are always referenced?
EDIT: So even if it is copying it.. Why can't I change it? It would net me the wrong result but you can change members of constants just not the constant themselves. For example you can do this:
class A {
var b = 5
}
let a = A()
a.b = 6
Your guess is true.
By accessing blockStatus, you are creating a copy of it, in this case, it's a constant copy (iterators are always constant).
This is similar to the following:
var numbers = [1, 2, 3]
for i in numbers {
i = 10 //cannot assign here
}
References:
Control Flow
In the example above, index is a constant whose value is automatically set at the start of each iteration of the loop.
Classes and Structures
A value type is a type that is copied when it is assigned to a variable or constant, or when it is passed to a function. [...] All structures and enumerations are value types in Swift
Methods
Structures and enumerations are value types. By default, the properties of a value type cannot be modified from within its instance methods.
However, if you need to modify the properties of your structure or enumeration within a particular method, you can opt in to mutating behavior for that method. The method can then mutate (that is, change) its properties from within the method, and any changes that it makes are written back to the original structure when the method ends. The method can also assign a completely new instance to its implicit self property, and this new instance will replace the existing one when the method ends.
You can opt in to this behavior by placing the mutating keyword before the func keyword for that method:
You could loop through the array with an index
for index in 0..<statuses.count {
// Use your array - statuses[index]
}
that should work without getting "cannot assign"
If 'Y' in this case is a protocol, subclass your protocol to class. I had a protocol:
protocol PlayerMediatorElementProtocol {
var playerMediator:PlayerMediator { get }
}
and tried to set playerMediator from within my player mediator:
element.playerMediator = self
Which turned into the error cannot asign 'playerMediator' in 'element'
Changing my protocol to inherit from class fixed this issue:
protocol PlayerMediatorElementProtocol : class {
var playerMediator:PlayerMediator { get }
}
Why should it inherit from class?
The reason it should inherit from class is because the compiler doesn't know what kind your protocol is inherited by. Structs could also inherit this protocol and you can't assign to a property of a constant struct.
What kind of type coercion does groovy support?
I saw map coercion and closure coercion. Are there any other?
And what is the difference between type coercion and type inference?
For example
def i = 1000 // type infere to Integer
i = 1000000000000 // type infere to Long or is this type coercion?
Groovy can use dynamic untyped variables whose types are assigned at runtime. Type inference refers to the automatic deduction of the type of an expression.
In your example: def i = 1000 assigns an instance of java.lang.Integer to the variable
Few simple tests as examples:
assert "Integer" == 1000.class.simpleName
assert "Long" == 1000000000000.class.simpleName
assert "BigDecimal" == 3.14159.class.simpleName
assert "Float" == 3.14159f.class.simpleName
According to Groovy's documentation: "Closures in Groovy work similar to a "method pointer", enabling code to be written and run in a later point in time".
Also, when working on Collections of a determined type, the closure passed to an operation on the type of collection can be inferred.
Type coercion is in play when passing variables to method of different type or comparing variables of different types. In comparing numbers of different types the type coercion rules apply to convert numbers to the largest numeric type before the comparison. So the following is valid in Groovy.
Byte a = 12
Double b = 10
assert a instanceof Byte
assert b instanceof Double
assert a > b
The Groovy "as" keyword is used in following situations:
used for "Groovy" casting to convert of value of one type to another
used to coerce a closure into an implementation of single method
interface
used to coerce a Map into an implementation of an interface, abstract, and/or concrete class
In addition to type coercion in maps and closures, there is also "Groovy Truth", which evaluates a type in an expression as TRUE or FALSE.
null is false
empty String, Map, or Collection is false
Zero number is false
if ( list ); if ( string ), if ( map )
same as writing if (list != null && !list.isEmpty()) ...
References:
Static Groovy and Concurrency: Type Inference in Action
http://groovy.dzone.com/articles/static-groovy-and-concurrency-3
Groovy Operator Overloading
http://groovy.codehaus.org/Operator+Overloading