Re: Some compiler issues and a request for debugging/design help

Dustin Voss <[email protected]> Tue, 18 Jun 2013 17:51:56 -0700
Newsgroups gmane.comp.lang.dylan.gwydion.devel
Message-ID <[email protected]>
There is one big problem and an ancillary issue that keep limited collection types from being useful in Dylan.

1. The default for the fill: argument to make(<mutable-sequence>) and subclasses is #f. That means, if you use a limited sequence whose valid elements do not include #f, you have to always specify a non-#f fill: argument.

2. This is particularly problematic when using collection functions such as map or choose, or even iteration. These can generate new sequences as part of their operation, and naturally, they don’t know about using anything other than #f for the fill: argument. So everything they generate would be illegal.

I know how to solve this, but it requires some (backwards-compatible) DRM and dylan library changes.

1. The limited(<mutable-sequence>) function needs to include a default-fill: keyword, where you can specify the default fill.
2. The <mutable-sequence> make/initialize functions need to use this value instead of #f if the user does not supply a fill: keyword.
3. The type returned by the limited(<mutable-sequence>) function needs to include a fill-element getter so that, when a copy of a limited collection with a non-default fill: is instantiated, it can be given the same non-default fill: and act like the original.
4. The returned type also needs an element-type getter to allow library code to validate new elements before attempting to add them to the collection.
5. type-for-copy, map, etc. need to make use of this information.


On Jun 16, 2013, at 7:11 AM, Paul R. Potts <[email protected]> wrote:

> Hello Dylanistas, happy Father's Day!
> 
> Being unemployed now I'm doing some coding for my own learning/fun recently… blogging about it here:
> 
> http://praisecurseandrecurse.blogspot.com/2013/06/objective-c-day-5.html
> 
> Struggling with object-oriented designs, I have in the past found it very useful to implement my ideas in Dylan to get my design thinking straightened, and then port it to a different implementation language, working around missing features. So I've been doing that with my little implementation of the old Mac "Polar" game -- porting it from Objective-C to Dylan as I think about what an ideal, textbook-concise implementation would look like.
> 
> If you aren't familiar with Polar, the idea is that your avatar is a penguin, walking around on a 4x24 grid of ice. The squares that make up the world can be empty, ice blocks, mountains, trees, hearts, bombs, and houses. They have rules to interact like so:
> 
> - The penguin can walk on ice and through trees (forested tiles)
> - ice blocks, hearts, and bombs, if pushed by the penguin onto empty squares, will slide, and continue sliding until they collide with a non-empty square
> - if an ice block is pushed against some other non-empty square it is crushed and disappears
> - if a bomb hits a mountain, it blows up the mountain and both bomb and mountain disappear
> - if a heart hits a house, it disappears, and that gets you closer to your goal of winning the board; when all the hearts have been cleared from the board, the stage is done
> 
> I've implemented this in Dylan using classes -- empty classes with no slots, just so that I can dispatch on their types, as in a collide method that takes singleton heart and house will do one thing, a collide method that takes singleton mountain and bomb will do something else. I'll attach my source in case you would like to look at it/play with it. Notes on that below.
> 
> I've been playing  with the 2012.1 compiler and have come across a few things. Maybe these are all well known, maybe they are actually not bugs and I just misunderstand, or maybe there are workarounds. I would be happy to take a crack at fixing some if I can, although it seems that maybe people far smarter than me have tried and failed -- what is the state of the art as it were in getting the compiler or run-time up under some kind of debugging environment? The way runtime errors just terminate, without giving line numbers, I find less than helpful.
> 
> It looks like there are still a lot of issues with making collection types. For example, I want to make a type-union of singletons and use it as the basis of a collection, where my constants like $the-bomb are instances of simple classes defined at the module level:
> 
> define constant <tile-or-false> = type-union(
>    singleton( $the-bomb ), singleton( $the-empty ),
>    singleton( $the-heart ), singleton( $the-house ),
>    singleton( $the-ice-block ), singleton( $the-mountain ),
>    singleton( $the-tree ) );
> 
> If I instantiate an array at the top level:
> 
> define constant <board-type> = limited( <vector>, of: <tile-or-false>,
>    init-value: $the-empty, size: 96 );
> 
> The compiler accepts this fine but at run-time I get a crash like so:
> 
> #f is not of type {<union>: {<union>: {<union>: {<union>: {<union>: {<union>: {<bomb>}, {<empty>}}, {<heart>}}, {<house>}}, {<ice-block>}}, {<mountain>}}, {<tree>}}
> #f is not of type {<union> 0x476ed0}
> Breaking into debugger.
> Trace/BPT trap: 5
> 
> It seems that including the singleton( #f ) is absolutely necessary to even create the limited type. I'm guessing that internally it is constructed containing #f before the init-value is actually applied, but it seems to me that this is a leaking implementation details?
> 
> Next, it looks like some work has been done on collections recently but some of them still seem pretty broken. If I define a limited 2-D array like so:
> 
> define constant <board-type-2D> = limited( <array>, of: <tile-or-false>,
>    init-value: $the-empty, dimensions: #( 4, 24 ) );
> 
> If I instantiate one, it always comes out as a simple object vector with actual size zero, and trying to set any element crashes.
> 
> ELEMENT outside of range: 0
> ELEMENT outside of range: 0
> Breaking into debugger.
> Trace/BPT trap: 5
> 
> Also, it appears that if I have a constant like
> 
> define constant $board-dim-y = 4;
> 
> I can't use it as something to pass in the dimensions: parameter.
> 
> So I'm still having to fake a 2-D array with my own indexing calculations (which works OK).
> 
> Let's say I then have my fake 2-D array type:
> 
> define constant <board-type> = limited( <vector>, of: <tile-or-false>,
>    init-value: $the-empty, size: 96 );
> 
> I want to use that type as a slot type:
> 
> define class <model> (<object>)
>    slot board :: <board-type>;
>    slot penguin-pos :: <pos>;
>    slot penguin-dir :: <dir>;
>    slot heart-count :: <integer>;
> end;
> 
> that also fails in a not-very-helpful manner. (I think that's a failure on my part -- <board-type> is actually not an instantiable class type, so my bad). I've wound up using plain old <array> for slot board and that seems to work.
> 
> Let's see, various other less-than-helpful crashes… oh, if I accidentally use a constant instance of a class instead of a type in a method definition like so:
> 
> define method collide( model :: <model>, dir :: <dir>,
>    slider-pos :: <pos>, slider-tile :: <pushable-tile>,
>    next-pos :: <pos>, next-tile :: $the-empty )
> end;
> 
> That produces a crash at program start-up even if I never call that method… ugh (it should read "next-tile == $the-empty -- my bad again, but ugh).
> 
> Let's see, what else… oh, if I have a crash I get _no_ output from format-out, even if I have printed hundreds of lines before the crash; I was not easily able to locate any kind of equivalent of flush()?
> 
> So I've made some progress but I still have a crashing bug -- line 252. If I un-comment that line, I get a crash in setTileAtXYDebug() in what looks to me like the kind of array element setter call that the program makes hundreds of times. In this case it is driven specifically by the collide method at line 153, the collision between bombs and mountains. In fact it really should just call setTileAtPos, but I was so confused by why this particular set of calls were always crashing that I made a separate method just to try to figure out what was different here.
> 
> Maybe my somewhat muddled design will become clear. The goal was to try, basically, to use the dispatch mechanism entirely for the penguin push and collide methods. I've had some difficulty here. I was getting errors about dispatch being determined by "arbitrary and capricious --?" rules. I think the current compiler is up to this if I can avoid colliding with certain mis-features.
> 
> Basically, the bigger design issue is that I want to use classes to represent the tile behaviors, and so I can dispatch on singletons -- I don't care about instances as the tiles have no state. But given that interactions take place on a board and can cross the entire board, class methods have to be able to access the board and look up and alter arbitrary tiles. So I'm kind of torn. I've played with giving tiles their own coordinates, with using actual instances for tiles and replacing them as they changed, but that also seems silly. I'd be curious as to whether any of you see a cleaner, simpler design in here struggling to get out. Note also how I'm passing both positions and types to the collide and push methods. That seems really sub-optimal but I wasn't sure if I could ask the compiler to dispatch on wrapped-up objects that contained both positions and singleton instances… but maybe that would be cleaner?
> 
> Thanks for any conversation… it's keeping my mind off the realities of my job search while I wait to here results back from various interviews/applications.
> 
> Paul
> 
> p.s. the color syntax highlighting in vim on Ubuntu for .dylan files is very nice -- anyone figured out how to get that working on MacOS X?
> 
> <arctic-slide.dylan><arctic-slide.hdp><arctic-slide.lid><library.dylan>
> 
> _______________________________________________
> hackers mailing list
> [email protected]
> https://lists.opendylan.org/mailman/listinfo/hackers
_______________________________________________
hackers mailing list
[email protected]
https://lists.opendylan.org/mailman/listinfo/hackers