How do I make generic memoization in Crystal? - crystal-lang

I want to define a generic memoizing wrapper in Crystal.
I have the following crystal code:
module Scalar(T)
abstract def value: T
end
class ScSticky(T)
include Scalar(T)
def initialize(sc : Scalar(T))
#sc = sc
#val = uninitialized T
end
def value: T
#val ||= #sc.value
end
end
In other words I want ScSticky to call the underlying Scalar(T) only once and return the cached output for all subsequent calls.
However, the above approach doesn't work if T is Int32
For instance, when wrapping this class
class ScCounter
include Scalar(Int32)
def initialize
#val = 100
end
def value: Int32
#val += 1
#val
end
end
ScSticky(ScCounter.new).value will always be equal to 0 (as I understand it, because the unitialized Int32 is actually initialized with 0 value)
I would highly appreciate some help with this problem
Upd: It seems that the proper way to implement this is using nil , however I have problems with understanding how exactly such implementation should look. I also want to be able to memoize .value method even if it returns nil (In other words, if the T is a nilable type)

You are using an unsafe feature "uninitialized", which means, "keep whatever was there in memory previously" (in theory the value is random and possibly invalid, in practice you often end up with 0 anyway -- but it's still not guaranteed at all).
The short story about the uninitialized feature is please never use it.
This behavior wouldn't surprise you if you wrote #val = 0 -- and that's kinda what you wrote.
You must define #val : T? = nil -- to make it nilable (have this separate possible value of nil, which is its own type - Nil).
You may have thought that unitialized brings nil into the picture, but it definitely doesn't.
In response to your comment about also including nil into possible values, here's a full solution, which, instead of Nil, uses a unique "sentinel" struct, that the user can never create.
module Scalar(T)
abstract def value: T
end
private struct Sentinel
end
class ScSticky(T)
include Scalar(T)
#val : T | Sentinel = Sentinel.new
def initialize(#sc : Scalar(T))
end
def value: T
val = #val
if val.is_a?(Sentinel)
#val = #sc.value
else
val
end
end
end
class ScCounter
include Scalar(Int32)
def initialize
#val = 100
end
def value: Int32
#val += 1
end
end
sc = ScSticky.new(ScCounter.new)
p! sc.value #=> 101
p! sc.value #=> 101

Related

How to handle redundant types in Crystal?

I'm using the crystal language, and it's been great so far. Unfortunately, I feel like my code is becoming a bit too messy with types everywhere.
For example:
# ---------=====----++---
# Grab characters
# ---------=====----++---
def handle_character_list(msg, client)
result = {} of String => Array(Tuple(Int64, String, String, Int16, Int8)) | Int32 | Int16 | Int64 | String
result["characters"] = db.query_all "select character_id, character_name, DATE_FORMAT(created, '%b:%d:%Y:%l:%i:%p') AS created, level, cc from rpg_characters where user_id = ? ", client.user_id,
as: {Int64, String, String, Int16, Int8}
result["max_char_slots"] = client.user_data["max_char_slots"]
puts result
end
While looking at the db.query_all method, it says:
returns an array where the value of each row is read as the given type
With the aforementioned above, why do I need to explicitly set those types again to my result variable, if they are going to be returned?
I feel like I'm doing something wrong, and any advice/insight is appreciated.
The first thing that jumps out at me is the size of the type of your hash. You seem to be using Hash the same way as in Ruby. Don't.
In Ruby, or other dynamic languages, Hashes or objects are used as generic data containers, almost like unnamed classes. In Crystal, hashes have a single type for the key, and a single type for the value, which makes them unsuited for the task. You want to tell Crystal more about the structure of your data, so you don't have to keep repeating that.
The first thing to do is look at the result object. It can simply be transformed into a record Result:
record Result,
characters: Array({Int64, String, String, Int16, Int8}),
max_char_slots: Int32
the method then becomes
def handle_character_list(msg, client)
sql = <<-SQL
SELECT character_id, character_name, DATE_FORMAT(created, '%b:%d:%Y:%l:%i:%p') AS created, level, cc
FROM rpg_characters
WHERE user_id = ?
SQL
characters = db.query_all sql, client.user_id, as: {Int64, String, String, Int16, Int8}
max_char_slots = client.user_data["max_char_slots"]
Result.new(characters, max_char_slots)
end
However, by looking at the method it might be that this Result record is only used in one place - to return data from this method. In that case it's unlikely you want to give it a more formal name. In this case you could use a NamedTuple. They're a bit like an anonymous record.
def handle_character_list(msg, client)
sql = <<-SQL
SELECT character_id, character_name, DATE_FORMAT(created, '%b:%d:%Y:%l:%i:%p') AS created, level, cc
FROM rpg_characters
WHERE user_id = ?
SQL
{
characters: db.query_all(sql, client.user_id, as: {Int64, String, String, Int16, Int8}),
max_char_slots: client.user_data["max_char_slots"]
}
end
Going further, we can see that a "Character" is also a type:
class Character
getter id : Int64
getter name : String
getter created : Time
getter level : Int16
getter cc : Int8
def initialize(#id, #name, #created, #level, #cc)
end
end
We can then use the DB.mapping to define how the Character class looks in the database.
class Character
DB.mapping({
id: Int64,
name: String.
created: Time,
level: Int16,
cc: Int8
})
def initialize(#id, #name, #created, #level, #cc)
end
end
This definition is equivalent to the previous one because DB.mapping automatically generates getters for us.
def handle_character_list(msg, client)
sql = <<-SQL
SELECT character_id, character_name, created, level, cc
FROM rpg_characters
WHERE user_id = ?
SQL
{
characters: db.query_all(sql, client.user_id, as: Character),
max_char_slots: client.user_data["max_char_slots"]
}
end
Going even further, I'd extract that into two methods, each one doing just one thing, and I'd probably make client.user_data more type safe:
def characters_for_user(user_id)
sql = <<-SQL
SELECT character_id, character_name, created, level, cc
FROM rpg_characters
WHERE user_id = ?
SQL
db.query_all(sql, user_id, as: Character)
end
def handle_character_list(msg, client)
{
characters: characters_for_user(client.user_id),
max_character_slots: client.user_data.max_char_slots
}
end
This is just my thought process on how I'd write the code you've shown. I've made a lot of assumptions about your code and database which might be wrong (i.e. "created" is a DATETIME in mysql). I'm attempting to show a thought process, not a finished solution. Hope that helps.

What is a proc in Crystal Lang?

I read the documentation on Procs in the Crystal Language book on the organizations’s site. What exactly is a proc? I get that you define argument and return types and use a call method to call the proc which makes me think it’s a function. But why use a proc? What is it for?
You cannot pass methods into other methods (but you can pass procs into methods), and methods cannot return other methods (but they can return procs).
Also Proc captures variables from the scope where it has been defined:
a = 1
b = 2
proc = ->{ a + b }
def foo(proc)
bar(proc)
end
def bar(proc)
a = 5
b = 6
1 + proc.call
end
puts bar(proc) # => 4
A powerful feature is to convert a block to Proc and pass it to a method, so you can forward it:
def int_to_int(&block : Int32 -> Int32)
block
end
proc = int_to_int { |x| x + 1 }
proc.call(1) #=> 2
Also, as #Johannes Müller commented, Proc can be used as a closure:
def minus(num)
->(n : Int32) { num - n }
end
minus_20 = minus(20)
minus_20.call(7) # => 13
The language reference explains Proc pretty well actually:
A Proc represents a function pointer with an optional context (the closure data).
So yes, a proc is essentially like a function. In contrast to a plain block, it essentially holds a reference to a block so it can be stored and passed around and also provides a closure.
A Proc is simply a function/method without a name. You can pass it around as a variable, and it can refer to variables in it's enclosing scope (it's a closure). They are often used as a way of passing method blocks around as variables.

Python 2: someIterator.next() vs. next(someIterator) :Python 3

In Python 2 iterators offer .next(), a callable method:
it = iter(xrange(10))
it.next()
> 0
it.next()
> 1
...
In Python 3 one has to use the built-in function next():
it = iter(range(10))
next(it)
> 0
next(it)
> 1
...
Is this just "syntactic sugar"? Like making it more obvious to use next() by moving it into the built-in functions? Or does any advanced concept hide behind this change?
You are directly asking about PEP 3114
consider the following code:
class test:
def __iter__(self):
return self
def next(self):
return "next"
def __next__(self):
return "__next__"
x = test()
for i,thing in enumerate(x):
print(thing)
if i>4:
break
in python 2 next is printed but in python 3 __next__ is printed, since the method is being called implicitly it makes way more sense to match other implicit methods such as __add__ or __getitem__, which is described in the PEP.
If you are planning on using next explicitly then much like len, iter, getattr, hash etc. then python provides a built in function to call the implicit function for you. At least... after PEP 3114. 😀
Also, the next built-in allows you pass a default value if you don't want to throw an error if the iterable is finished which is frequently useful:
it = iter([])
x = next(it, "pls don't crash")
which couldn't really be standardized for a .next() method call. As well objects such as a random number generator may define a .next (next number) method without necessarily wanting to be an iterator which could have left ambiguity / confusion.

Dynamically change data in where clause using Spock

I have to iterate over some map and for every key, to make checks based on the current element,so I tried:
class TestSuite extends Specification {
#Shared
def elements
def setupSpec{
elements = ['a.txt':1,'b.txt':2]
}
#Unroll
def 'test for #first and #second'() {
expect:
true
where:
[first, second] << [elements.keySet(), elements[first].findResults { key, value->
key.substring(key.indexOf('.') + 1)
}].combinations()
}
}
but Spock fails and says that first is unknown.
How can I do that so that the two values to be in the name of the test so in unroll to see their values?
Edited
I have to say that your code does not make much sense. I have hesistated to just downvote your question instead of replying. There is so much wrong with this question. Just shows a lack of effort in formulating your question.
The first red flag is that you did not even try to compile this code. You have a map with String keys and integer values:
your map litteral does not compile. key/value pairs are separated by commas in groovy.
You call methods of this map that (Map#key() ?) are not in the API.
Then, there are two possibilities, either your imaginary key() method returns the key, which does not make any sense, because first IS the key already, or key() returns the value, which is really bad naming. But bad naming is not all, because then you call toUpperCase() on an integer.... This is a mess!
Nevertheless I am going to show you how you can base the value of a where variable on the value of another where variable, because that's the core part of your question, with
import spock.lang.*
class MyFirstSpec extends Specification {
//elements needs to be Shared to be used in a where block
#Shared
def elements = ['a':1,'b':2]
#Unroll
def 'test for #first and #second and #third'() {
expect:
true
where:
first << elements.keySet()
and:
second = elements[first]
third = first.toUpperCase()
}
}
​resulting in
- test for a and 1 and A
- test for b and 2 and B

Python - null object pattern with generators

It is apparently Pythonic to return values that can be treated as 'False' versions of the successful return type, such that if MyIterableObject: do_things() is a simple way to deal with the output whether or not it is actually there.
With generators, bool(MyGenerator) is always True even if it would have a len of 0 or something equally empty. So while I could write something like the following:
result = list(get_generator(*my_variables))
if result:
do_stuff(result)
It seems like it defeats the benefit of having a generator in the first place.
Perhaps I'm just missing a language feature or something, but what is the pythonic language construct for explicitly indicating that work is not to be done with empty generators?
To be clear, I'd like to be able to give the user some insight as to how much work the script actually did (if any) - contextual snippet as follows:
# Python 2.7
templates = files_from_folder(path_to_folder)
result = list(get_same_sections(templates)) # returns generator
if not result:
msg("No data to sync.")
sys.exit()
for data in result:
for i, tpl in zip(data, templates):
tpl['sections'][i]['uuid'] = data[-1]
msg("{} sections found to sync up.".format(len(result)))
It works, but I think that ultimately it's a waste to change the generator into a list just to see if there's any work to do, so I assume there's a better way, yes?
EDIT: I get the sense that generators just aren't supposed to be used in this way, but I will add an example to show my reasoning.
There's a semi-popular 'helper function' in Python that you see now and again when you need to traverse a structure like a nested dict or what-have-you. Usually called getnode or getn, whenever I see it, it reads something like this:
def get_node(seq, path):
for p in path:
if p in seq:
seq = seq[p]
else:
return ()
return seq
So in this way, you can make it easier to deal with the results of a complicated path to data in a nested structure without always checking for None or try/except when you're not actually dealing with 'something exceptional'.
mydata = get_node(my_container, ('path', 2, 'some', 'data'))
if mydata: # could also be "for x in mydata", etc
do_work(mydata)
else:
something_else()
It's looking less like this kind of syntax would (or could) exist with generators, without writing a class that handles generators in this way as has been suggested.
A generator does not have a length until you've exhausted its iterations.
the only way to get whether it's got anything or not, is to exhaust it
items = list(myGenerator)
if items:
# do something
Unless you wrote a class with attribute nonzero that internally looks at your iterations list
class MyGenerator(object):
def __init__(self, items):
self.items = items
def __iter__(self):
for i in self.items:
yield i
def __nonzero__(self):
return bool(self.items)
>>> bool(MyGenerator([]))
False
>>> bool(MyGenerator([1]))
True
>>>