Eiffel: Is there a way to test a given Generic parameter of a Class without any attached instance of it? - generic-programming

Is there a way to test a given Generic parameter of a Class without any attached instance of it?
class BAG[G -> MOUSE]
feature --
discriminate
do
if G.conforms_to (MAMMAL) then
io.putstring ("you gave me a real animal")
elseif G.conforms_to (COMPUTER_ACCESSORY) then
io.putstring ("Seems you don't like animals such as computers")
else
io.pustring ("Still dont know what you are dealing with")
end
end

You've almost nailed it. The missing part is curly braces and parentheses:
if ({G}).conforms_to ({MAMMAL}) then
io.put_string ("You gave me a real animal.")
elseif ({G}).conforms_to ({COMPUTER_ACCESSORY}) then
io.put_string ("Seems you don't like animals such as computers.")
else
io.put_string ("Still don't know what you are dealing with.")
end
Explanation:
{FOO}, where FOO is a type name, stands for a type object. It works for any type, including formal generics, thus {G} and {MAMMAL}.
The syntax {FOO}.bar is reserved for non-object calls. But here we want an object call on the type object. Therefore, {G} is enclosed in parentheses: ({G}).conforms_to (instead of {G}.conforms_to).

Related

Compile error when trying to use jooq any operator in kotlin

I have problems using jooq with kotlin and the any clause.
Given the following:
I have a Database field in a postgreSQL database which is an array
I have search parameters which are a List of Strings
I want to use jooq any operator to search in the array
I have the following code which is not working:
fun findAll(
someArrayListOfStrings: List<String>?
): List<SomeDTO> {
val filters = ArrayList<Condition>()
filters.add(TABLE.SOME_FIELD.eq(DSL.any(someArrayListOfStrings)))
}
Here I want to dynamically create filters (jooq Conditions) to be added to some SQL statement. It should work to look if SOME_FIELD (PostgreSQL Array Type) contains one of the following strings using the ANY clause (PostgreSQL jooq binding). However I get the following compile-time error:
None of the following functions can be called with the arguments supplied:
public abstract fun eq(p0: Array<(out) String!>!): Condition defined in org.jooq.TableField
public abstract fun eq(p0: Field<Array<(out) String!>!>!): Condition defined in org.jooq.TableField
public abstract fun eq(p0: QuantifiedSelect<out Record1<Array<(out) String!>!>!>!): Condition defined in org.jooq.TableField
public abstract fun eq(p0: Select<out Record1<Array<(out) String!>!>!>!): Condition defined in org.jooq.TableField
But my function call should match the third type where QuantifiedSelect is used.
I looked for hours on the internet but was not able to find any solution. Any site I found told me to try the solution I already have. Does anyone have an idea what I could try and why it does not work?
Thank you!
The method you're calling here is DSL.any(T...), which takes a generic varargs array (in Java). You're passing a List<String>, so this binds T = List<String>, which doesn't satisfy the type constraint on the eq() method.
But even if you changed that to an Array<String>, it wouldn't work because the jOOQ ANY operator doesn't do the exact same thing as the PostgreSQL any(array) operator. So, just resort to either plain SQL templating:
condition("{0} = any({1})", TABLE.SOME_FIELD,
DSL.value(someArrayListOfStrings.toTypedArray()))
Or just use the IN predicate
TABLE.SOME_FIELD.in(someArrayListOfStrings)

Kotlin: Type inference failed: Not enough information to infer parameter E in fun <E> <init>(): kotlin.collections.ArrayList<E>

I declared a variable like this:
var G: Array<MutableList<Int>> = Array(0) { ArrayList() }
Kotlin gives me the following error:
Kotlin: Type inference failed: Not enough information to infer parameter E in fun <E> <init>(): kotlin.collections.ArrayList<E> /* = java.util.ArrayList<E> */
Please specify it explicitly.
It means Kotlin can't infer the type for the ArrayList which should be Int. So I add Int explicitly for the ArrayList like following:
var G: Array<MutableList<Int>> = Array(0) { ArrayList<Int>() }
Kotlin says - Remove explicit types arguments
In this case, Kotlin is ambivalent about how to act.
So is it possible to write code without explicitly declaring the type of ArrayList?
As discussed here,
The way it works curretly is that whenver we encounter a collection in Kotlin, we load a Kotlin version of this class (e.g. kotlin.Collection) instead of a Java version (java.util.*). Using the type java.util.Collection leads to a warning from the Kotlin compiler, because Kotlin's type checker is designed to distinguish between read-only and mutable collections.
So you can try to use like this,
var G = arrayOf<MutableList<Int>>()
Moreover, here are some a good stuff to know for you.
Kotlin says - Remove explicit types arguments
Kotlin doesn't (you can see there's no warning in https://pl.kotl.in/7v1h5Yobu). It's probably the IDEA plugin which does. If you look at https://youtrack.jetbrains.com/issues/KT?q=Remove%20explicit%20types%20arguments, you can see there are quite a few false positives. It may be worth checking if yours is actually one of them and posting a new issue if it isn't.
var G = Array<MutableList<Int>>(0) { ArrayList() }
should work without warning from IDEA either.

Limit Array to multiple specific data types

I am working on refactoring a tool to OOP in PS5.
I have a number of classes. Sets can contain other Sets as well as Packages. Packages can contain other Packages and Tasks. And Tasks can contain other Tasks. For example...
Set1
Package1.1
Task1.1
Set2
Package2.1
Task2.1
Set2A
Package2A
Task2A.1
Task2A.2
Package2.2
Task2.2
Set3
Package3.1
Task3.1
Task3.1A
I plan to have Set, Package and Task classes, but there are a number of different Tasks with some common features and some unique, so I will have a base Task class that is then extended by the various final task classes.
My question relates to the data structure to contain the nested objects. If each class could only contain the next deeper type everything would be easy; the variable to hold the Packages in a Set could be an array of Packages, i.e. [Package[]]$Contents.
I could make it super flexible and just do an array; [Array]$Contents, but that allows for invalid items like strings and such.
Alternatively I could have some sort of Root class, with Sets, Packages and Tasks all extended that, and final Tasks then extending Tasks, and use[Root[]]$Contents or some such. But that might not be possible and it would still allow for adding a Task to a Set, since a final Task class would ultimately be extending from Root.
So, the question becomes, can you define an array that accepts multiple possible types but is still limited, something like [Set/Package[]]$Contents? Or is there perhaps a totally different way to define a variable that limits the valid members? An Enum seems to have potential, but it seems like they are limited to strings as I tried
enum AllowedTypes {
[Array]
[Strings]
}
and that in no good.
Or am I best of just using an Array and validating what I am adding in the Add method of each Class? I can see a possible solution there where I have overloaded Add methods in the Set class, one that takes a Set, one that takes a Package, and one that takes a generic object and throws an error to log. Assuming that the more specific overload method takes priority rather than everything going to the generic method since it's technically valid. Or perhaps that generic method won't even work since the collection of overloaded Add methods technically can't collapse to one valid choice because a Set is both a [Set] and a [PSObject] I guess.
PetSerAl, as countless times before, has provided an excellent (uncommented) solution in a comment on the question, without coming back to post that solution as an answer.
Given the limits of code formatting in comments, it's worth presenting the solution in a more readable format; additionally, it has been streamlined, modularized, extended, and commented:
In short: a PowerShell custom class (PSv5+) is used to subclass standard type [System.Collections.ObjectModel.Collection[object]] in order to limit adding elements to a list of permitted types passed to the constructor.
class MyCollection : System.Collections.ObjectModel.Collection[object] {
# The types an instance of this collection
# is permitted to store instance of, initialized via the constructor.
[Type[]] $permittedTypes
# The only constructor, to which the permitted types must be passed.
MyCollection([Type[]] $permittedTypes) { $this.permittedTypes = $permittedTypes }
# Helper method to determine if a given object is of a permitted type.
[bool] IsOfPermittedType([object] $item) {
return $this.permittedTypes.Where({ $item -is $_ }, 'First')
}
# Hidden helper method for ensuring that an item about to be inserted / added
# / set is of a permissible type; throws an exception, if not.
hidden AssertIsOfPermittedType([object] $item) {
if (-not $this.IsOfPermittedType($item)) {
Throw "Type not permitted: $($item.GetType().FullName)"
}
}
# Override the base class' .InsertItem() method to add type checking.
# Since the original method is protected, we mark it as hidden.
# Note that the .Add() and .Insert() methods don't need overriding, because they
# are implemented via this method.
hidden InsertItem([int] $index, [object] $item) {
$this.AssertIsOfPermittedType($item)
([System.Collections.ObjectModel.Collection[object]] $this).InsertItem($index, $item)
}
# Override the base class' SetItem() method to add type checking.
# Since the original method is protected, we mark it as hidden.
# This method is implicitly called when indexing ([...]) is used.
hidden SetItem([int] $index, [object] $item) {
$this.AssertIsOfPermittedType($item)
([System.Collections.ObjectModel.Collection[object]] $this).SetItem($index, $item)
}
# Note: Since the *removal* methods (.Remove(), .RemoveAt())
# need to type checking, there is no need to override them.
}
With the above class defined, here's sample code that exercises it:
# Create an instance of the custom collection type, passing integers and strings
# as the only permitted types.
# Note the (...) around the type arguments, because they must be passed
# as a *single argument* that is an *array*.
# Without the inner (...) PowerShell would try to pass them as *individual arguments*.
$myColl = [MyCollection]::new(([int], [string]))
# OK, add an [int]
# .Add() implicitly calls the overridden .InsertItem() method.
$myColl.Add(1)
$myColl.Add('hi') # OK, add a [string]
# OK, override the 1st element with a different [int]
# (though a [string] would work too).
# This implicitly calls the overridden .SetItem() method.
$myColl[0] = 2
# OK - insert a [string] item at index 0
$myColl.Insert(0, 'first')
# $myColl now contains: 'first', 2, 'hi'
# Try to add an impermissible type:
$myColl.Add([long] 42)
# -> Statement-terminating error:
# 'Exception calling "Add" with "1" argument(s): "Type not permitted: System.Int64"'

How to apply a function before comparison in an esqueleto query

For a query simple as that
runDb . select . from $ \cell -> do
where_ $ cell ^. CellCode ==. val "x"
return cell
I want to apply a function before the comparison of the field value with "x". The reason is that the cell code has trailing spaces in the database and nothing easier than trimming them away, e.g. with strip from Data.Text. However, my initial approach of using fmap (twice) resulted in
No Instance for (Functor SqlExpr)
I know that there are functions provides by Esqueleto, like just, that accomplish similar things specifically (I couldn't find the implementation of just, though).
Is there a way to apply any function on the packed value?
While writing: in my specific case, I just might want to use like.
EDIT: Added the specific function I want to apply.
What kind of function to you want to apply?
Here is how someone added the ability to call the chr() function in a query:
https://github.com/krisajenkins/esqueleto/commit/fa1d1c888770e297fef52d76b6cb68342a6c0376
If it is a built-in function (or a user-definable function), perhaps you can do something similar.
See here for a post that adds the postgresql function trim:
import Database.Esqueleto.Internal.Sql
trim :: (IsString s) => SqlExpr (Value s) -> SqlExpr (Value s) -> SqlExpr (Value s)
trim pattern target =
unsafeSqlFunction "trim" (unsafeSqlBinOp "FROM" pattern target)
(If you're not using postgres, you may need to consult the documentation from your database to find if it supports something similar.)
unsafeSqlFunction can be used to import any function your database supports, but it is unsafe because you have the responsibility to make sure the type signature is actually what your database expects. The name will be copied literally to your SQL.
unsafeSqlBinOp is similar, but it defines a binary operation: unsafeSqlBinOp "FROM" "a" "b" is translated into the SQL "a" FROM "b".
With this, you should be able to do:
runDb . select . from $ \cell -> do
where_ $ trim " " (cell ^. CellCode) ==. val "x"
return cell

eval(): can't assign to function call

Yet another question for my cookie clicker...
Here is the code I made that produces an error:
cps=cps+bcps[buych]
c=c-bprice[buych]
eval(buych)=eval(buych)+1
cps is a variable,
c is a variable,
b1,b2,b3,b4,b5 are variables
buych is a string (Which I get from an input() command)
bcps and bprice are maps (shown below)
bcps = {"b1":'1',
"b2":'5',
"b3":'10',
"b4":'20',
"b5":'25'}
bprice = {"b1":'10',
"b2":'20',
"b3":'30',
"b4":'40',
"b5":'50'}
So, what I'm trying to achieve is:
-Take the input string value, which will be 'b1','b2', 'b3', 'b4', 'b5'
-Increase cps buy it's value within bcps
-Decrease c by it's value within bprice
-Use eval to convert the 'b#' to b#
-Increase b# by 1
When running the script, I do not get error text in the python shell. Instead, I get a pop-up that says "Can't assign to function call". The error is highlighted as the first white space before the eval(). I really don't get this, as I have no functions within the error.
Thanks for reading,
Cookie Monster
eval(buych) is a function call -- you're calling the function eval(). You can only assign to variables, you can't assign to the result of a function.
You need to concatenate the variable into a valid assignment expression, and exec that:
exec(buych + '=' + buych + '+1')
eval is for evaluating expressions, you have to use exec to execute a whole statement.
But if you find yourself needing to do this, you're usually doing something wrong. Instead of computing variable names and expressions, you should use lists or dicts so you can refer to them using an index or key.

Resources