What runtime code can I write to extract the type notations as defined by Sorbet, to display to the user? - sorbet

Is there a way to inspect the type notations in ruby/sorbet?
I actually want to display those values in a form somewhere and instead of hardcoding in values like "this can be nil or a String", I want to instead use the code I've defined by the sig { ... }.
Example pseudo-code:
class User
extend T::Sig
sig { returns(T.nilable(String)) }
def foo
...
end
end
Sorbet.return_type(User, :instance_method, :foo) # returns '[nil, String]' or similar

I believe this is what I am looking for:
T::Utils.signature_for_method
In action:
T::Utils.signature_for_method(User.new.method(:daily?)).return_type
=> #<T::Types::Union:0x0000000132198da8
#types=[#<T::Types::Simple:0x0000000132198f60 #name="TrueClass", #raw_type=TrueClass>, #<T::Types::Simple:0x0000000132198e98 #name="FalseClass", #raw_type=FalseClass>]>

Related

Possible to create class aliases dynamically?

Got this class:
class Mass-lb is Mass {
method new(Rat:D() $value = 1.0) {
self.bless(
:abbr('lb'),
:base_value(453.59237),
:$value,
);
}
}
I have created aliases like this:
class Mass-lbs is Mass-lb { }
class Mass-pound is Mass-lb { }
class Mass-pounds is Mass-lb { }
class Mass-pnds is Mass-lb { }
But I'd prefer to do something like this:
my #lb-syn = < lbs pounds pound pnds >;
for #lb-syn {
EVAL 'class ::("Mass-$_") is Mass-lb {}';
}
This throws an error:
Name ::("Mass-$_") is not compile-time known, and can not serve as a package name
PHP has a built-in for creating aliases: https://www.php.net/manual/en/function.class-alias.php
I couldn't find anything similar for raku.
In RakuAST there's a class that you can call to create a new type. But that the RakuAST branch hasn't landed yet.
Until then, your approach using EVAL is valid, you just need to make it a bit simpler:
class Mass-lb { }
BEGIN "constant Mass-$_ = Mass-lb".EVAL
for <lbs pounds pound pnds>;
my $mlb = Mass-lbs.new;
Make sure the aliases are created at BEGIN time.
No need to subclass, you can use a constant for aliasing.
Since constants are our by default, they're visible outside of the EVAL.
Alternatively, you could use raku Physics::Unit and Physics::Measure...
use Physics::Unit;
use Physics::Measure :ALL;
# define a new custom Unit
Unit.new( defn => 'lbm', names => <Mass-lb Mass-lbs Mass-pound Mass-pounds Mass-pnds> );
say GetUnit('Mass-lbs').names; #[Mass-lb Mass-lbs Mass-pound Mass-pounds Mass-pnds]
# use the Unit in a Measure
my $mass = ♎️'42 Mass-pnds';
say $mass; #42Mass-lb
say $mass.^name; #(..Mass) ...class name
# convert to another Unit
my $kgm = $mass.in: 'kg';
say $kgm; #19.05087954kg
# convert back
say $kgm.in: 'Mass-pound'; #42Mass-lb
# raku Rats mean that the back conversion is identical
say $kgm cmp $mass; #Same
# with % or abs Error
my $mass2 = ♎️'42 Mass-pnds ±3%';
say $mass2; #42Mass-lb ±1.26
say $mass2.in: 'kg'; #19.05087954kg ±0.5715263862
More info at github Physics::Unit and Physics::Measure...

Get hash keys or type of value in a hash

Coming from flowtype, I'm used to having things like $Keys<obj> to say 'the union of all the keys in obj'. Also, $PropertyType<obj, 'key'> to get the type of the value of obj[key].
I'm looking for something similar in Sorbet so I can make better use of generics.
Ideally I want the key param to be T.any(<keys in the hash>) and the return value to be typeof hash[:key]
This is what I'd like to happen:
# my_rbi.rbi
class SomeClass
Elem = type_member
sig { params(key: Keys[Elem]).returns(PropertyType<Elem, key>) }
def [](key); end
end
# somewhere else
sig { returns(SomeClass[{ my_key: String }]) } # add the hash as a generic
def context
# return the context
end
context[:my_key] # works, is a String!
context[:not_my_key] # fails
Is any of this possible? what are my options here?
Context:
My use case is types for graphql-ruby, in particular GraphQL::Query::Context. It essentially delegates [] to an internal hash, which I know the shape of. I'd like to pass a generic to GraphQL::Query::Context, like GraphQL::Query::Context[{ my_key: String }]. Then, in the def [] signature, I'd like to say what the available options for key are based on the generic hash.

Perl 6 multi methods never match expected signature

I have a class with two multi methods (multi submit).
I call my multi like this:
$perspective.submit(:message($message.content));
Which gets shipped off to my class:
my $perspective-api = API::Perspective.new(:api-key(%*ENV<PERSPECTIVE_API_KEY>));
proto method submit (|) {*}
multi method submit(Str :$message!, MODEL :#models = TOXICITY) {
my $score = $perspective-api.analyze(:#models, :comment($message));
say #models Z=> $score<attributeScores>{#models}.map: *<summaryScore><value>;
multi method submit(Str :$name!, MODEL :#models = TOXICITY) {
my $score = $perspective-api.analyze(:#models, :comment($name));
say #models Z=> $score<attributeScores>{#models}.map: *<summaryScore><value>;
}
However I always get the following response:
Died because of the exception:
Cannot resolve caller AUTOGEN(Rose::ContentAnalysis::Perspective:D: :message(Str)); none of these signatures match:
(Rose::ContentAnalysis::Perspective: Str :$message!, MODEL :#models = MODEL::TOXICITY, *%_)
(Rose::ContentAnalysis::Perspective: Str :$name!, MODEL :#models = MODEL::TOXICITY, *%_)
Despite my named argument (:message) being a Str as required and #models having a default declared.
Multiple dispatch works in two phases:
Considering the number of positional parameters and their types
If there are any where clauses, named parameters, or sub-signatures, doing a test bind of the signature to see if it would match
The second phase will reject the candidate if it fails to bind for any reason. One such reason, and I believe the cause of the issue here, is that the default value is wrongly typed. For example, in:
multi m(:#x = "not-an-array") { }
m()
We get an error:
Cannot resolve caller m(...); none of these signatures match:
(:#x = "not-an-array")
in block <unit> at -e line 1
But changing it to:
multi m(:#x = ["an-array"]) { }
m()
Works fine. (Note that while a default value uses =, it's actually a binding, not an assignment.)
In the case in the question there's this:
MODEL :#models = TOXICITY
Looking at the module source the code is taken from, I see:
enum MODEL is export (
<TOXICITY SEVERE_TOXICITY TOXICITY_FAST IDENTITY_ATTACK
INSULT PROFANITY SEXUALLY_EXPLICIT THREAT FLIRTATION
ATTACK_ON_AUTHOR ATTACK_ON_COMMENTER INCOHERENT INFLAMMATORY
LIKELY_TO_REJECT OBSCENE SPAM UNSUBSTANTIAL>
);
Thus TOXICITY is just an Int, but what's expected is a typed array of MODEL values.
Thus, if you do this:
multi method submit(Str :$message!, MODEL :#models = Array[MODEL](TOXICITY)) {
It should work.
I see two issues.
One is that you have two methods that are identical except for the name of one named parameter.
Named parameters can have aliases:
# V--------------V
multi method submit(Str :name(:$message)!, MODEL :#models = TOXICITY) {
my $score = $perspective-api.analyze(:#models, :comment($message));
say #models Z=> $score<attributeScores>{#models}.map: *<summaryScore><value>;
}
Note that :$message is really short for :message($message)
Now on the problem which actually prevents your code from working.
#models is a Positional, but you are assigning it a singular value in the signature.
Assign it a Positional, and it works:
(In this case it has to be of type Array[MODEL] because of the MODEL type declaration.)
# V---------------------V
multi method submit(Str :name(:$message)!, MODEL :#models = Array[MODEL](TOXICITY,)) {
my $score = $perspective-api.analyze(:#models, :comment($message));
say #models Z=> $score<attributeScores>{#models}.map: *<summaryScore><value>;
}

Access Instance-property in Coffeescript within nested

so I am using express within a node-app. As my app is getting bigger I want to put my routes into extra files. I seem to be able to get hold of the bugDB if I just get rid of the intermediate get object. But I can't access the bugDB in the inner object. Any suggestions? Maybe there is even a more nice code pattern for how to accomplish this more elegantly.
I would appreachate your help. Thanks in advance. (As I am not a native speaker I couldn't find others with a similar problem, if you know how to phrase the question better, please show me the way :) )
BUGROUTER.COFFEE
class BugsRouter
constructor: (#bugDB)-> // instance-variable with databaselink
return
get:{
allBugs: (req, res)=>
console.log "db", #bugDB // this gives me undefined
// is "this" in the get context?
#bugDB.allDocs {include_docs: true}, (err, response)->
res.json 200, response
}
module.exports = BugsRouter
SERVER.COFFEE
BugsRouter = require "./routes/BUGROUTER"
bugsRouter = new BugsRouter(bugDB)
console.log bugsRouter.bugDB # this is working
app.get "/bugs/all", bugsRouter.get.allBugs
Sub-objects don't work like that. When you say this:
class C
p:
f: ->
Then p is just a plain object that happens to be a property on C's prototype, it will have no special idea of what # should be inside f. And if you try to use a fat-arrow instead:
class C
p:
f: =>
then you're accidentally creating a namespaced class function called f so # will be C when f is called. In either case, saying:
c = new C
c.p.f()
is the same as:
c = new C
p = c.p
p.f()
so f will be called in the context of p rather than c.
You can get around this if you don't mind manually binding the functions inside get when your constructor is called:
constructor: (#bugDB) ->
#get = { }
for name, func of #constructor::get
#get[name] = func.bind(#)
This assumes that you have Function.bind available. If you don't then you can use any of the other binding techniques (_.bind, $.proxy, ...). The #get = { } trick is needed to ensure that you don't accidentally modify the prototype's version of #get; if you're certain that you'll only be creating one instance of your BugsRouter then you could use this instead:
constructor: (#bugDB) ->
for name, func of #get
#get[name] = func.bind(#)
to bind the functions inside the prototype's version of get rather than the instance's local copy.
You can watch this simplified demo to see what's going on with # in various cases, keep an eye on the #flag values to see the accidental prototype modification caused by not using #get = { } and #constructor::get:
class C1
get:
f: -> console.log('C1', #)
class C2
get:
f: => console.log('C2', #)
class C3
constructor: ->
#flag = Math.random()
for name, func of #get
#get[name] = func.bind(#)
get:
f: -> console.log('C3', #)
class C4
constructor: ->
#flag = Math.random()
#get = { }
for name, func of #constructor::get
#get[name] = func.bind(#)
get:
f: -> console.log('C4', #)
for klass in [C1, C2, C3, C3, C4, C4]
o = new klass
o.get.f()
Live version of the above: http://jsfiddle.net/ambiguous/8XR7Z/
Hmm, seems like I found a better solution after all:
class Test
constructor: ->
#testVariable = "Have a nice"
return
Object.defineProperties #prototype,
get:
enumerable :true
get:->
{
day: => #testVariable + " day"
week: => #testVariable + " day"
}
console.log (new Test()).get.day()
This allows me to call (new Test()).get.day() the way I wanted.
Live version at: JSFiddle

lua call function from a string with function name

Is it possible in lua to execute a function from a string representing its name?
i.e: I have the string x = "foo", is it possible to do x() ?
If yes what is the syntax ?
To call a function in the global namespace (as mentioned by #THC4k) is easily done, and does not require loadstring().
x='foo'
_G[x]() -- calls foo from the global namespace
You would need to use loadstring() (or walk each table) if the function in another table, such as if x='math.sqrt'.
If loadstring() is used you would want to not only append parenthesis with ellipse (...) to allow for parameters, but also add return to the front.
x='math.sqrt'
print(assert(loadstring('return '..x..'(...)'))(25)) --> 5
or walk the tables:
function findfunction(x)
assert(type(x) == "string")
local f=_G
for v in x:gmatch("[^%.]+") do
if type(f) ~= "table" then
return nil, "looking for '"..v.."' expected table, not "..type(f)
end
f=f[v]
end
if type(f) == "function" then
return f
else
return nil, "expected function, not "..type(f)
end
end
x='math.sqrt'
print(assert(findfunction(x))(121)) -->11
I frequently put a bunch of functions in a table:
functions = {
f1 = function(arg) print("function one: "..arg) end,
f2 = function(arg) print("function two: "..arg..arg) end,
...,
fn = function(arg) print("function N: argh") end,
}
Then you can use a string as an table index and run your function like this
print(functions["f1"]("blabla"))
print(functions["f2"]("blabla"))
This is the result:
function one: blabla
function two: blablablabla
I find this to be cleaner than using loadstring(). If you don't want to create a special function table you can use _G['foo'].
loadstring is not the answer here. For starters you would need a return in the string, and other details I won't go into.
THC4k has the right idea; if you have the function name in the variable x, then the call you want is
_G[x](arg1, arg2, ...)
Names are not unique, there can be many functions names foo in different namespaces. But _G['foo'] is foo in the global namespace.
It sounds like you want to do an 'eval', which is supported in Lua like so:
assert(loadstring(x))()
You'll probably want to concatenate the "()" onto x first, though.