endformationage has quit [Ping timeout: 240 seconds]
jemc has joined #ponylang
_whitelogger has joined #ponylang
jemc has quit [Ping timeout: 268 seconds]
papey_la1 has joined #ponylang
vaninwagen has joined #ponylang
endforma1 has quit [Quit: WeeChat 1.7]
anqur has joined #ponylang
anqur has left #ponylang [#ponylang]
vaninwagen__ has joined #ponylang
vaninwagen has quit [Disconnected by services]
vaninwagen__ is now known as vaninwagen
vaninwagen has quit [Ping timeout: 268 seconds]
papey_la1 is now known as papey
hakvroot has quit [Quit: No Ping reply in 180 seconds.]
hakvroot has joined #ponylang
vaninwagen has joined #ponylang
_andre has joined #ponylang
jemc has joined #ponylang
vaninwagen has quit [Ping timeout: 240 seconds]
autodidaddict has joined #ponylang
jemc has quit [Quit: WeeChat 1.4]
jemc has joined #ponylang
Praetonus has joined #ponylang
bimawa has joined #ponylang
<autodidaddict>
yet another newb question. I'm creating a class that has a field that is an array of actors... I want to store them as [Any tag]
<autodidaddict>
but I can't call push or delete without running into the receiver type error
<autodidaddict>
yes, I know I'm doing something stupid.. but every time I make any Pony progress, my Go day job seems to undo it :D
<Praetonus>
autodidaddict: Can you show an example of what you're trying to do?
<autodidaddict>
class Foo
<autodidaddict>
let _stuff : Array[Any tag]
<autodidaddict>
fun add_stuff(o : Any) => _stuff.push(o)
<Praetonus>
You need `fun ref add_stuff`
<autodidaddict>
oh (censored)
<autodidaddict>
I knew it was something stupid
<autodidaddict>
It's not intuitive (yet) to go from a complaint about Array[Any tag] not matching this->Array[Any tag] to "you didn't put ref in your fun"
<Praetonus>
Yeah, some error messages are cryptic the first few times you encounter them
<Praetonus>
We're always trying to improve them, though
<autodidaddict>
btw, I'm loving the ability to have strongly-typed, generic actors. I'm brewing up a blog post on this
<autodidaddict>
I just need a time machine so I can spend more time playing with Pony without sacrificing my other jobs :)
shelajev has joined #ponylang
amclain has joined #ponylang
shepheb has joined #ponylang
<shepheb>
If actor A1 calls A2 10 times in a row, and each such call will result in a call to a behavior on A3, will those 10 calls arrive in order?
<shepheb>
I'm not sure if that's included in causal messaging, since those messages are in parallel, sort of, rather than causing each other.
<autodidaddict>
if I remember right, all actors process their mailboxes "in order".. but if you have 2 actors calling the same one concurrently, I don't think you'd be able to control how those messages are interleaved... (disclaimer: this is largely assumption based on my usage of Akka)
<Praetonus>
shepheb: Is A2 the one sending messages to A3?
<shepheb>
yeah.
<Praetonus>
Then yes, they will happen in order
<jemc>
autodidaddict: Pony has some extra guarantees about message order than most other actor languages have
<jemc>
specifically, Pony has "causal message order"
<autodidaddict>
nice
<shepheb>
Praetonus: I mean, if A1 has a for-loop that calls A2 10 times, and each call to A2 results in a call to A3, A3 will see them in the same order?
<shepheb>
it'd be really nice if that were true, but the A2->A3 messages aren't causally linked.
<jemc>
shepheb: can you elaborate on "A2 results in a call to A3"?
<shepheb>
in particular, if A2 takes varying amounts of time before calling A3, they can't reorder?
<shepheb>
let me write a little test program that duplicates the flow.
<jemc>
if A1 sends some messages directly to A2 in a specific order, A2 is guaranteed to handle them in that same order
<jemc>
the same is true for A2 -> A3, provided that the messages are being sent directly with no varying actors in the middle
<jemc>
that is, if it "looks like a diamond" somewhere along the path, you lose the ordering guarantee
<shepheb>
it certainly does here, at least in some paths.
<shepheb>
A2 might delegate two or three more actors down, passing the A3 along as the "sink".
<shepheb>
so yeah, no guarantees for me.
<shepheb>
that's alright, I can work around it, probably with partial application.
<shepheb>
so that A3 gets (key, value) instead of just (value).
<shepheb>
yeah, my test program confirms: with another actor thrown in then the order at A3 is undefined.
<shepheb>
hrm. I'm crashing ponyc somehow, ast != NULL assertion. I assume that means a parser error that isn't captured properly. in that vein, can I partially-apply a behavior?
<jemc>
yes, we sometimes see assertion failures - those assertions usually amount to runtime type checks that are failing, which is one big motivating reason for trying to get the Pony compiler ported to Pony (where we can have the AST typechecking statically)
<jemc>
if you can file a ticket with a sample program that produces that error, we can work on fixing it
<shepheb>
I'm working on narrowing it down now. the code I thought might be suspicious is apparently fine.
<shepheb>
sorry I couldn't cut it down to a small example, I tried all kinds of things but it was reporting the errors correctly, not crashing.
<jemc>
shepheb: based on the backtrace, it's erroring out when processing an `as` statement
<jemc>
(looking at the `expr_as` part of the backtrace)
<jemc>
is the code you're compiling against open source? if so, can you link to it in the ticket?
<jemc>
otherwise, if you have access to lldb or gdb I could try to guide you through a few commands to help provide more info on where the issue might be...
<jemc>
autodidaddict: I noticed your examples in your medium article are game-oriented - you may have already seen this, but if not you'll probably find it interesting: https://github.com/jtfmumm/acolyte
<autodidaddict>
oh holy crap that's spectacular
<autodidaddict>
sweet. I like the pattern of the trait that can supply a default implementation based on data by exposing a data() method. Very slick
<shepheb>
jemc: the code is currently closed-source, and the code on Github is far, far behind the current one.
<shepheb>
jemc: interesting, let me try to repro again.
<shepheb>
I've added a repro case. the stacktrace looks different? Maybe it's still instructive.
<jemc>
awesome, thanks
<autodidaddict>
Is the idea behind a primitive with an apply to create a globally accessible, side-effect free function?
<autodidaddict>
I'm seeing that pattern in Acolyte as an implementation of the strategy pattern... various AI primitives are passed around as parameters.
<Praetonus>
autodidaddict: Not necessarily side-effect free since you can still pass mutable objects in, but yes, you can emulate free functions with primitives
<autodidaddict>
yeah, I didn't quite mean side-effect free. I meant "without violating Pony's safety guarantees"
obadz has quit [Ping timeout: 240 seconds]
<jemc>
autodidaddict: yeah, I'd summarize a primitive as a "global, stateless, singleton object", being used in a few different patterns
<autodidaddict>
having a primitive evaluate decisions on behalf of intelligent agents is very elegant... and definitely in the spirit of encapsulation over inheritance :)
obadz has joined #ponylang
<shepheb>
if I define an interface Foo be apply(...) shouldn't a partially-applied behavior on some other actor fulfill that interface?
<shepheb>
assuming the signatures match, I mean.
<jemc>
autodidaddict: I don't know if the acolyte codebase is doing this, but another nifty trick with primitives & the strategy pattern is to resolve the strategy at compile time, by passing the primitive as a type argument instead of a runtime argument
vaninwagen has joined #ponylang
<autodidaddict>
I think the strategy primitives are all being passed at runtime as constructor parameters to the agent actors, e.g. a goblin gets movementAI1 and attackAI2, etc
<autodidaddict>
by compile time, what would an example of that be?
<jemc>
basically, instead of `Goblin.create(MovementAI1, AttackAI2)`, it would look something like `Goblin[MovementAI1, MovementAI2].create()`
<jemc>
err.. the second one should be `AttackAI2` in the second example, but you get the picture
<autodidaddict>
ahh, right, so you could do `let speedyGoblin: Goblin[FastAI, AttackNinja]` and `let dufusGoblin: Goblin[SlowAI, AttackSloth]`
<autodidaddict>
that's beautiful
<autodidaddict>
what would the type signature look like on the actor?
<autodidaddict>
actor Goblin[AIType,AttackType]?
<autodidaddict>
not sure how you would then invoke apply on that primitive... unless you define an interface or trait like AIStrategy that has an apply method on it
<autodidaddict>
so .. actor Goblin[AIType: MovementStrategy, AttackType: AttackStrategy] ?
<autodidaddict>
hmm, that doesn't look right
<jemc>
yes, your second example is correct
<jemc>
where `MovementStrategy` is a trait or interface that all your movement strategies implement
<autodidaddict>
but you still have to pass in a reference to `MovementSloth` when you instantiate your goblin .. at runtime
<jemc>
anyway, it's pretty much equivalent to the runtime-based example, except for 1) it's all done at compile time, and 2) every different combination of type parameters actually creates a distinct type, rather than instantiating the same type
<jemc>
autodidaddict: no, not if your interface includes `new val create()`
<jemc>
(which all primitives implement)
<autodidaddict>
oooo
<autodidaddict>
I didn't realize primitives implemented create.. .I figured they were non-instantiable
<autodidaddict>
but they're more like singletons?
_andre has quit [Quit: leaving]
<jemc>
yeah, it's a singleton
<autodidaddict>
so, to invoke apply on my primitive passed in as type parameter MovementStrategy (which would be fulfilled by something like SlothStrategy), I would just ... call MovementStrategy(input) ?
<autodidaddict>
trying to build a sample... being assaulted by the receiver type failures :(
<autodidaddict>
first one I get is "type argument is outside its constraint" for: var slowGob: Goblin[SlowMovement] tag = Goblin[SlowMovement]
<jemc>
autodidaddict: it would be `AIType(input)`, but yes
<jemc>
autodidaddict: if you gist your larger code snippet, and the error you see, I can probably help you
endformationage has quit [Read error: Connection reset by peer]
endformationage has joined #ponylang
<autodidaddict>
I've got it narrowed down to my primitive not implementing the right create signature... searching the source now
<autodidaddict>
trait MovementAI fun apply(input: I32): I32 new create() primitive SlowMovement is MovementAI fun apply(input: I32): I32 => 12 * input
<autodidaddict>
val constructor is not a subtype of ref constructor
<shepheb>
the constructor needs to be new val create() => None I think.
<shepheb>
er, just new val create()
<autodidaddict>
ok, that did the trick
<autodidaddict>
weird syntax quirks I just haven't internalized yet
<autodidaddict>
thanks!
<autodidaddict>
what's the idiom for unit testing? create a separate (foo)_test.pony file for each .pony ... or create a test/ directory as a peer of src/ ?
<autodidaddict>
I also don't see anything about launching a test runner, etc.
<Praetonus>
autodiddadict: There is the ponytest package in the standard library for unit testing
papey has quit [Ping timeout: 255 seconds]
<Praetonus>
Standard library packages use a single _test.pony file to contain their unit tests
<autodidaddict>
ok
<autodidaddict>
and that test gets run during "ponyc" ?
<autodidaddict>
Looking for references on building your own library package... ponyc doesn't let me compile without a main
<shepheb>
general design questin.
<shepheb>
I'm working on a Gameboy emulator, and hoping to make most of its functionality async, partly for the experience and partly as a stress test (eg. can the runtime handle ~40M messages per second?)
<shepheb>
my plan for implementing the instructions was to describe them as a sequence of fundamental operations, and have the engine wait for each to complete before moving to the next.
<shepheb>
rather than coding them directly, which is hard when each memory access is async.
<shepheb>
but if an example instruction reads the byte at [PC], advances PC, reads register A, adds the result and sets it in A, I'm not sure how to pass the values from the "inputs" to the processing.
<shepheb>
oh, and it reads some flags too.
<shepheb>
I don't want to write each instruction as a mess three promises deep
<jemc>
autodidaddict: the standard library convention is that the `Main` actor in each package invokes ponytest and runs the tests
<jemc>
autodidaddict: I'm not a big fan of having the test code and library code in the same package, so I typically have another directory (package) for my tests
<jemc>
with the library not having any actual Main actor, but that's fine for a library - ponyc will only require/use `Main` in the toplevel source dir that's being compiled, which represents the program - the packages that the toplevel program uses need not have `Main` if they don't want to
<shepheb>
I suspect I'm ending up back where I've been before: I should be able to write a method on an actor that "blocks" until async work is done.
<shepheb>
when what's actually happening is that the compiler is transforming that to create new behaviors that chain the results together.
<shepheb>
but that's a lot of backend work to make that happen, and I'm sure there are some thorny implications for garbage collection and all the rest.
<jemc>
shepheb: in your current design, can you elaborate on which parts of your system are actors, what they do, and what data they hold?
<jemc>
also, in the gameboy semantics are all instructions executed in linear sequence?
<jemc>
(in my latter question I'm trying to get a feel for what parts of your system actually make sense to parallelize)
<jemc>
sometimes it makes sense to start with that question - which bits of processing do you envision as being parallelizable?
<shepheb>
as I said it was partly as an experiment.
<shepheb>
from first principles, the Gameboy has roughly four things going on in parallel: CPU, GPU, audio and D-pad input.
<autodidaddict>
regex question.. in Rust, I have the following code: let re = Regex::new(r"([+-]?\s*\d+[dD]\d+|[+-]?\s*\d+)").unwrap(); let matches = re.find_iter(drex); for m in matches { ... do stuff ... }
<shepheb>
I'm ignoring audio for now.
<autodidaddict>
when I apply that same regex in Rust, I only seem to be able to match 1 group... do I need to invoke something else on the match to continue matching?
<shepheb>
D-pad input is straightforward; poll SDL periodically and react.
<shepheb>
so the main parts the matter are the CPU and GPU.
<autodidaddict>
it's supposed to accept expressions like "3d6+10" or "2d20+5" etc.. the Pony code I'm using (following the example in the regex package docs) only turns up the first group... it says there are 2 matches, but matches(0) and matches(1) are just the first term
<shepheb>
the simple design that comes to mind is to have each be an actor, where the two slices of memory that are accessible to both are isos that get tossed back and forth.
<shepheb>
but I wanted to do something more tricky just to see what would happen.
<shepheb>
the answer so far has been "suffering"
<shepheb>
the back-and-forth is part of the Gameboy; certain parts of memory get "locked" during parts of drawing the screen.
<jemc>
I'd probably approach it something like: the CPU is an actor, with instructions as synchronous methods (`fun ref`) on it, and having an `access` behaviour that takes a lambda and implements the access pattern that I laid out here: https://ponylang.gitbooks.io/pony-patterns/content/async/access.html
<jemc>
the GPU can call the `access` behaviour to do a "locked
<jemc>
" transaction on that memory
<jemc>
and you can add other wrapper behaviours for convenience in doing common operations, if you need them
<jemc>
anything that the GPU needs to capture in its lambda that it sends to the CPU actor would have to be sendable
<autodidaddict>
for my previous statement about regex: I'm trying to take that regex and produce an unknown number of capture groups
<autodidaddict>
so it could match 3d10 or 3d10+1d20+5
<autodidaddict>
so I guess I'm looking for the Pony equivalent of Rust's find_iter(subject)
<shepheb>
once you don't know how many capture groups you're going to see, that's parsing.
<shepheb>
what about splitting the whole string on + and -, and handling each piece separately?
<autodidaddict>
that could work. I just like how it was a 1-liner in Rust
<jemc>
autodidaddict: the `Match` object you get back in Pony contains the `end_pos`, so you could use that to start matching again from that `end_pos` in an iterator style
<jemc>
if you come up with a nice iterator for that which makes sense, we could potentially add it to the standard library (via the RFC process)
<jemc>
so, in essence I'm saying: "be the change you wish to see in the standard library" :D
<autodidaddict>
haha. nice
<jemc>
if you're having trouble making it work, let us know, and we can probably assist with the design
<jemc>
right now I don't think I have enough context without reading the rust docs, which I don't have time for atm
<autodidaddict>
it does basically what you're asking for.. it runs a match over and over again as an interator, until there are no more calls to next
<autodidaddict>
I'll see if I can come up with something un-hideous
<autodidaddict>
is there an example of an iterator pattern I can look at for reference?
<autodidaddict>
hmm.. Regex apply raises an error when there's no match... so how does one do a "while no error is raised" loop?
<jemc>
do a grep for `is Iterator[` in the standard library, and anything you see there would be a good place to start
<jemc>
what you probably want to do is to design a `class MatchIterator is Iterator[Match]`
<jemc>
which takes a `String` and an optional initial offset as arguments to its constructor
<autodidaddict>
yeah... that's what I figured. digging now ;)
<jemc>
looking at `class FileLines` in the files package might be a good similar example
<jemc>
but the essence of an iterator is implementing a `has_next(): Bool` method and a `next(): Match ?` method
<jemc>
where you'd let the error get raised through to the caller if `next()` failes
<jemc>
if you want to cheat in your proof of concept, you could have `has_next` always return true, and it would still work in a `for` loop, but that's something you'd want to fix before offering it into the standard library
vaninwagen has quit [Ping timeout: 246 seconds]
<autodidaddict>
holy crap that works
<autodidaddict>
I'll clean it up and not fake has_next and then maybe you can help me figure out how to get it into stdlib
autodidaddict has quit [Ping timeout: 260 seconds]
Matthias247 has quit [Read error: Connection reset by peer]
<SeanTAllen>
autodidaddict was kind enough to write a blog post about his very early experiences with Pony... "My First Pony"... "https://www.ponylang.org/blog/2017/07/my-first-pony/" if you are new to Pony and would be interested in writing a similar blogpost let me know.
endformationage has quit [Quit: WeeChat 1.7]
malthe has quit [Remote host closed the connection]
malthe has joined #ponylang
autodidaddict has joined #ponylang
obadz has quit [Ping timeout: 255 seconds]
obadz has joined #ponylang
autodidaddict has quit [Ping timeout: 260 seconds]