<crystal-gh>
[crystal] zamith closed pull request #385: Allows divisions with the num1/(num2) syntax (master...master) http://git.io/bYrW
crystal-gh has left #crystal-lang [#crystal-lang]
crystal-gh has joined #crystal-lang
<crystal-gh>
[crystal] zamith opened pull request #389: Allows divisions with the num1/(num2) syntax (master...divisions-dot-syntax) http://git.io/bGID
crystal-gh has left #crystal-lang [#crystal-lang]
bcardiff has quit [Quit: Leaving.]
crystal-gh has joined #crystal-lang
<crystal-gh>
[crystal] asterite pushed 3 new commits to master: http://git.io/bGmr
<crystal-gh>
crystal/master fb15d9b Zamith: Allows divisions with the num1/(num2) syntax...
<crystal-gh>
crystal/master c1631f9 Zamith: Corrects the way we fix it
<asterite>
I’m not sure I understand what you mean
<asterite>
The `stubs.has_key?({{name}})` is executed at runtime
<asterite>
not at compile time
<asterite>
I mean, method_missing is invoked at compile time when the compiler can’t find a method, and then it invokes that macro which generated the method definition for that method
<asterite>
and then it’s like a regular method
<zamith>
you're right, I'll just have to raise something in the form of a test failure output
<zamith>
about the comment in the gist
<zamith>
have you read it?
<asterite>
Commented
<zamith>
thanks
<asterite>
I’m not sure you will get very far that way, the hash will mix all types
<zamith>
also, what I was looking for (but probably won't use) was just a call to super on method_missing
<zamith>
that will fail on compile time
<asterite>
But how can you know at compile time what’s in stubs?
<zamith>
I can't, it will be empty and it will always raise
<zamith>
:p
<zamith>
about the hash being a mix of all types
<zamith>
would having a hash of Procs be better?
<zamith>
I want you to be able to say that for this double, method x should respond with y
<asterite>
A hash will always mix all types
<zamith>
if I have {} Symbol => Int32 that will mix all types?
<asterite>
You can probably start by making Double a generic type, so two Doubles won’t mix their types
<asterite>
But then types from a single hash will be mixed… you’d need to think of that. Maybe a macro could help
<asterite>
In fact, it’s probably better to do `double(:foo, {x: 1, y: “hi”}` and that would be a macro that generates a class that responds to those methods
<asterite>
BUT, there’s currently no way to make that class be declared outside the method
<asterite>
It’s a hard problem :)
<zamith>
the other problem is that it limits the place where stubs can be defined
<zamith>
I would like to define them anywhere on the tests, not just on the initialization of the double
<asterite>
I know
<zamith>
what is the issue with having the stubs be Symbol to Procs?
<zamith>
is that a mix of all types as well?
<asterite>
It’s the same, the procs types will mix
<zamith>
oh, procs have types:?
<asterite>
then you invoke one of them, you could get any type of them
<asterite>
Of course :-)
<zamith>
what type is that?
<zamith>
the return vale?
<zamith>
*value
<asterite>
>> f = ->{ 1 }; f.class
<DeBot>
asterite: ( -> Int32)
<zamith>
>> f = ->{ "string"}; f.class
<DeBot>
zamith: ( -> String)
<zamith>
yeah
<zamith>
what I thought
<zamith>
I need a generic container for this stubs, though
<asterite>
>> Function(Int32, Int32).new { |x| x + 1 }.class
<DeBot>
asterite: (Int32 -> Int32)
<asterite>
;)
<zamith>
*these
<asterite>
There goes another easter egg
<zamith>
wow
<zamith>
what was that?
<asterite>
What we would need is to have something like named tuples
<zamith>
Function takes the input and output types?
<zamith>
I'm not very used to typed languages, sorry
<asterite>
>> [1, 2, 3].map &.to_s
<DeBot>
asterite: ["1", "2", "3"]
<jhass>
struct Baz; end; fun foo(bar : Baz**); if I do foo(out bar) I get garbage in bar in case foo doesn't touch it, it's not null initialized I think, I have to bar :: Bar*; foo(pointerof(bar))
<jhass>
bug or intended?
<asterite>
>> `rm -rf /`
<DeBot>
asterite: Sorry, I can't let you do that.
<asterite>
(just showing debot to waj and ggiralde, they just found about it :-P)
<jhass>
asterite: it answers to queries ;P
<asterite>
!roul
<asterite>
Meh
<jhass>
heh, I didn't enable the toys here :P
<zamith>
asterite: any big pitfall in method_missing?
<waj1>
jhass: you are awesome! did you know that!? :)
<jhass>
thanks, you too ;)
<zamith>
it seems very dynamic for crystal
<asterite>
jhass: right, out just allocated stack space, doesn’t initialize anything
<jhass>
I think it should
<jhass>
I know it's slower
<jhass>
but it's such a common pitfall
<asterite>
zamith: what I mean is, if you could do double(“foo”, magic(x: 1, y: “hey”)), with “magic” creating an instance of a class/struct that has x and y getters, where x has type Int32 and y has type String, then the types wouldn’t mix
<zamith>
can't that be done right now?
<zamith>
with macros and structs, or record?
<asterite>
jhass: we might zero the memory then
<jhass>
:)
<asterite>
zamith: well, that’s the problem: “magic” would need to first define the type and then instantiate it, but if you are inside a method you are not allowed to define a type
<zamith>
it's only class level, right?
<asterite>
Not quite, magic would expand to “record Magic1, x, y; Magic1.new(x, y)”
<asterite>
That in theory could work, but “record” expands to “struct Magic1; end”, but if that is inside a “def” you would get a compile error
<asterite>
And even if we allow this, how can you generate successive Magic1, Magic2, … on each call? You would need to have some sort of state
<asterite>
I believe eventually we will come up with a way to make this work, without magic in the compiler… but we always go slowly, gathering many use cases, until we find a way that covers all of those cases
<asterite>
and probably without making the language too complex
<zamith>
just to make sure, is there any problem with a hash of all types, besides being slower?
<asterite>
You will probably pass that double to some method you want to test
<asterite>
for example
<asterite>
def abs_plus_one(x); x.abs + 1; end
<asterite>
d = double(“x”, {abs: 1, another: “hey”})
<asterite>
abs_plus_one(d)
<asterite>
=> undefined method “abs” for String
<asterite>
Because “abs”, in the method missing, can now return Int32 or String
<asterite>
jhass: waj was just saying, would you like to do a web playground, similar to http://play.rust-lang.org/ ? That would really be awesome
<zamith>
asterite: I tried that situation, and x.abs.class is Int32, but I do get the error because there is no overload of + on String
<zamith>
or is it Int32 only on runtime?
<asterite>
On runtime it could be String or Int32, the compiler doesn’t know because you are getting that from a hash, the compiler doesn’t know what will be in the hash at runtime
<jhass>
asterite: my box is maybe a bit overloaded, but given that I've taken down the Ruby DeBot there might be enough room for it
<jhass>
I'll see
<jhass>
I might need a PostgreSQL or DBI binding :P
<asterite>
Well, it’s an open source project ;-)
<zamith>
you'd have to do (x.abs as Int32) + 1
<zamith>
I get that it's an issue
<zamith>
I was just wondering if we could roll with it for the time being
<zamith>
until we come up with a better way to implement that magic thing
asterite has quit [Read error: Connection reset by peer]
asterite has joined #crystal-lang
<jhass>
asterite: there's another issue that needs to be fixed first: crystal eval doesn't clean up
<asterite>
remove temporaries or what?
<jhass>
it's probably no issue for the sandbox since that mounts a new tmpfs each time
<jhass>
but I somehow always accumulate a bunch of crystal-run-xxx in /tmp
<asterite>
I guess we need to delete those
<jhass>
yeah, and while we're at it I think the testsuite should cleanup better too
<jhass>
crystal_mkdir_ptest...
<jhass>
compiler_spec_output....
<jhass>
crystal-spec-output....
<jhass>
foo.... didn't verify if those are by crystal yet
<asterite>
Probably :)
bcardiff has quit [Quit: Leaving.]
<zamith>
asterite: from method_missing can I get the file and line that called the method?
waj1 has quit [Quit: Leaving.]
<asterite>
zamith: I don’t think so, but with regular macros you can
<asterite>
I forgot to add it for method missing too
<zamith>
so it's a bug?
<zamith>
:p
<asterite>
it’s a missing feature ;)
<zamith>
I wanted to add that to the fail call
<zamith>
right now it's poiting to method missing, which is not very useful
<asterite>
Right
<zamith>
btw, I'm going with the hash of mixed values for now
<asterite>
Yes, method_missing is very experimental right now, I think it must be done in a different way
<asterite>
Ok
<zamith>
and when/if the language has a better way, we can adapt
<zamith>
also, I was thinking of having it as an external lib
<asterite>
You’ll use that for the demo?
<zamith>
or do you think mocks should be in the stdlib?
<zamith>
yeah, I probalby will, to show method missing
<asterite>
It would be nice to have them in the stdlib, because they are very common and useful
<zamith>
you can probaly see what I'm trying to do
<zamith>
but I'm getting lost in typecasting hell
<zamith>
is there a good way to do this in a typed fashion?
<zamith>
From what I could understand I the initializer must have a variable of type T, right?
<asterite>
Yes
<asterite>
Or you must say it like this: Double(Int32).new(...)
<asterite>
If you don’t say it, then the compiler will try to infer it from the new/initialize
<zamith>
ok
<asterite>
`@stubs = ni : T` would be enough, I guess
<zamith>
that doesn't work when I don't pass stubs
<zamith>
it goes to the default value, but T doesn't get set
<zamith>
defined I mean
<asterite>
You can try doing with like this: def self.new(name); Double(Nil).new(name, nil); end
<asterite>
However, I don’t see what T would be used for, if then you have that `receives`
<asterite>
Also, if @stubs starts nil, where do you initialize it, and with what type?
<zamith>
yes, that is the problem I'm facing
<asterite>
In short, I don’t think you’ll be able to make it with the current language
<asterite>
But that’s just my guess, maybe you come up with something really clever :)
<zamith>
if instead of passing a hash, for stubs, I have an Array(Stub)
<zamith>
where Stub would be a class in it's own right, would it help
<zamith>
I could then have a NullStub, instead of the type Nil
<zamith>
that would inherit from Stub
<asterite>
I don’t think so, all Stubs will have their types merged
<zamith>
even if the Stub is a generic?
<zamith>
does it even make sense?
<asterite>
A generic instantiated on what type?
<asterite>
Then you have the method “receives"
<asterite>
Imagine you do this: double(“foo”).receives(“a”).and_returns(1).receives(“b”).and_returns(1.5)
<asterite>
How can you make the compiler know that when invoke “a” on that double, the type will be Int32 and not Float64?
<zamith>
receives would need to the return type
<zamith>
I'll try to fiddle with the dsl, but I guess at some point we have to stop infering, and ask for the return type
<asterite>
I don’t know what you mean by “stop infering and ask for the return type"
<zamith>
I meant to ask the user for the return type
<zamith>
but I'm not sure
<asterite>
I think the only way it would work if you initialize the double in a single call, that would be a macro instantiation
<asterite>
but then you can’t configure the double
<asterite>
Or you would need to create the double by saying: these are the methods and their types
<asterite>
Something like that
<asterite>
But even if you do that, you can’t stub existing methods, you can only use doubles, and for that you need to force your design to use dependency injection… which i kind of hate :)
<zamith>
what do you mean by stubbing existing methods?
<asterite>
But… I personally don’t like stubs, they bind your tests to the implementation in a very strong way
<zamith>
oh
<zamith>
I'd rather have a dummy or a test double and inject that into the SUT
<zamith>
if your class does have hard dependencies like that, intead of being inject than it is a problem
<zamith>
but let's defer that for now
<zamith>
:p
<asterite>
Well, Time.now is a classic example
<zamith>
is there a type for Value | Reference?
<asterite>
In Java and C# you end up with IDateProvider, IClock or some interface so you can mock it
<asterite>
Lots of indirections just to make the code testeable
<zamith>
you can have def method(time = Time); time.now; end
<asterite>
Yes, Object, but it’s unusable for anything you want to do with it (meaning: compiler crash)
<zamith>
then you can just inject a dummy instead of time
<asterite>
Maybe it’s not that bad :)
<asterite>
In one spec we came up with a little trick
<asterite>
If you have a class that does Time.now
<asterite>
In your spec you can subclass it, and define a class Time inside that new subclass, and define a self.now method that returns the value that you want in your spec
<zamith>
But I have the GOOS book at home I'll look into it and get back at you
<zamith>
I don't want to admit defeat
<zamith>
:p
<zamith>
This limitations make sense, but you might want to mock web requests to 3rd party APIs, or calls to 3rd party libraries, if you have an adapter for them, or something like that