Incoming: An example game in Elm

138 views
Skip to first unread message

David Foster

unread,
Jan 24, 2014, 10:46:41 PM1/24/14
to elm-d...@googlegroups.com
I created a space-invaders style game with Elm over the MLK weekend which may be of interest:

This could be added as another game example on the Examples page at:

Jeff Smits

unread,
Jan 25, 2014, 5:33:29 AM1/25/14
to elm-discuss
This is great! I tried it, and it's a pretty hard game :)
Are you interested in criticism?

David Foster

unread,
Jan 25, 2014, 1:38:55 PM1/25/14
to elm-d...@googlegroups.com
> Are you interested in criticism?

Sure. I already know there are a couple of style issues that are likely, such as preferring 2-space indent. Or perhaps use of the |> or <| operators.

From a type organization standpoint the code itself notes there are some hacks around difficulty representing individual sprite types distinctly yet sharing state. In other languages I would use subtyping but Elm lacks subtyping polymorphism, so that's not an option here.

Jeff Smits

unread,
Jan 26, 2014, 7:12:32 AM1/26/14
to elm-discuss
For the sprites, would it be enough to use extensible records?
type Sprite = { position : Point, stype : SpriteType }
type ShortLivedSprite = { Sprite | timeToLive : Float }
I looked through the source code, not actually reading much in details but I saw some recurring patterns.
I would advise to split your file into multiple files. Something like the labels you wrote in comments now: types, constants, main, render, input, update, util.
The nice thing about multiple files is you can make more functions top-level, just not export them. Then you can look at your functions without syntactic noise around and compare them more easily. Factor out common patterns.

--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Evan Czaplicki

unread,
Jan 26, 2014, 11:38:03 AM1/26/14
to elm-d...@googlegroups.com
Very cool! Nice work :D

I think Jeff is right about how to share code between similar records. Elm has structural typing which is like duck typing that is checked statically. I just revamped the Pong in Elm article to look nice and use extensible records in the model and it lets you share code in both the update functions and renderer.

David Foster

unread,
Jan 27, 2014, 10:24:26 PM1/27/14
to elm-d...@googlegroups.com
I did take a look at using structural records. For example:

type Sprite a = { a | x : Float, y : Float }
type Player = Sprite {} -- NOTE: indistinguishable from Bomb and Shot
type Bomb = Sprite {}
type Shot = Sprite {}
type Explosion = Sprite { timeToLive : Float }

-- Does not work. Needs a fixed 'a' to fill in 'Sprite a'
spriteList : [Sprite]

Unfortunately this does not allow me to make a list of sprites in general. I would have to make a new list for every new kind of sprite and process them separately in the main game loop. This is reasonable for a small number of sprite types (≤5) but not a lot (≥10).

I looked at using an algebraic type to get the same effect. For example:

type Sprite = { x : Float,
                y : Float,
                d : SpriteData }

data SpriteData = Player
                | Bomb
                | Shot
                | Explosion { timeToLive : Float }

spriteList : [Sprite] -- okay

This actually seems to work, at the cost of complicating matching somewhat when processing the list of sprites.

However both of the prior 2 approaches eliminate the "sprite type" as a first-class entity, which was very useful in the original program. In the original program I could navigate from a sprite to its sprite type, and from there to attributes about the sprite type (principally the associated image's name and dimensions).

I could duplicate the "sprite type" information in each sprite but that just feels like a waste of space.

I would advise to split your file into multiple files. Something like the labels you wrote in comments now: types, constants, main, render, input, update, util. 
The nice thing about multiple files is you can make more functions top-level, just not export them.

I will take a look at this. At first blush it wasn't clear that I could have a module designate which of its functions were public (and therefore exported).

Dandandan

unread,
Jan 28, 2014, 3:35:29 AM1/28/14
to elm-d...@googlegroups.com
-- This does work, or is this not what you want? (Sprite takes one type argument).
spriteList : [Sprite a]
spriteList = []

Op dinsdag 28 januari 2014 04:24:26 UTC+1 schreef David Foster:

Jeff Smits

unread,
Jan 28, 2014, 4:30:55 AM1/28/14
to elm-discuss
@Daniël:
The problem comes up when you add different sprites to a list with different things for `a`.

@David:
Module imports are described on the syntax page. And here's an example of a module naming what it exports. I think the default is export everything.

You make good points about using records. But I guess eventually if it comes down to distinguishing between different sprites you will always need an ADT. (or something more complex)

Dandandan

unread,
Jan 28, 2014, 5:00:18 AM1/28/14
to elm-d...@googlegroups.com
Okay, so even
spriteList = [{x = 0, y = 0}], doesn't work, as the compiler can't figure out the type of a (expecting a, but the type is {} in this case).

But
spriteX : Sprite a -> Float
spriteX = .x

and

spriteListXS : [Sprite a] -> [Float]
spriteListXS = map .x

work for any sprite, so wouldn't it be nice if you could mix different records (with at least x and y) in a list or any data type, wouldn't it?

Op dinsdag 28 januari 2014 10:30:55 UTC+1 schreef Jeff Smits:
Reply all
Reply to author
Forward
0 new messages