On Sat, Apr 22, 2017 at 12:37 AM, Stefan Matthias Aust <[email protected]>
wrote:
>
> *My first question*: Is there any way to create a constrained type like
> "something that has a `no` field?
>
>
Right now, I have a lot of nearly identical code like this:
>
> findStar : StarNo -> Game -> Maybe Star
> findStar no game =
> List.head (List.filter (\star -> star.no == no) game.stars)
>
> findPlanet : PlanetNo -> Star -> Maybe Planet
> findPlanet no star =
> List.head (List.filter (\planet -> planet.no == no) star.planets)
>
You could use record pattern matching to create generic functions but in
the above case that won't work because the container name is different.
Here is how it would have looked if the container name would have been the
same (in this case `children`)
findChild : Int -> { b | no : Int, children : List { a | no : Int } } ->
Maybe { a | no : Int }
findChild no parent =
List.head (List.filter (\child -> child.no == no) parent.children)
I used Int for the `no` but you you can leave that also as a parameter
(e.g. `c`)
> *Second question*: Is there an easy way to replace an element in a list?
>
> You can create a generic helper like
swapIfSameNo : { a | no : Int } -> { a | no : Int } -> { a | no : Int }
swapIfSameNo a b =
if a.no == b.no then
a
else
b
and then the updates would be simpler
updatePlanetInStar : Star -> Planet -> Star
updatePlanetInStar star planet =
{ star | planets = List.map (swapIfSameNo planet) star.planets }
The same comments from above apply. If you have a generic name for the
container (children) you can make this a generic updateChild function that
would work on both.
> Sidenote: I'd love to use
>
> { game | stars[x].planets[y] = planet }
>
> and let the compiler deal with all that stupid boilerplate code. I realize
> that for this to work, I'd have to use `Array` (a rather unsupported type)
> instead of `List` but that would be fine. That way, I could alias my `no`
> to indices. By the way, do I really had to create my own `nth` function for
> Lists?
>
What make you think Array is unsupported?
Regarding indexes in arrays (or lists for that matter), it would be lovely
to be able to say things like that but it is unsafe. You might be using an
out of bounds index.
There are no solutions in contexts like these, only tradeoffs. Elm choses
to make things more verbose and more explicit in order to guarantee safety.
That's the tradeoff that Elm chose.
> *Third question*. When implementing `BuildDefense`, I need to convert the
> no's to records and this could fail. Instead of working with `Maybe`, I use
> a `Result Error a` type and `Error` is a sum type that contains all the
> errors that could occur. However, I get some really ugly chaining…
>
> buildDefense : TeamNo -> StarNo -> PlanetNo -> Int -> Game -> Result Error
> Game
> buildDefense teamNo starNo planetNo amount game =
> findTeamR teamNo game
> |> Result.andThen
> (\team ->
> findStarR starNo game
> |> Result.andThen
> (\star ->
> findPlanetR planetNo star
> |> Result.andThen
> (\planet ->
> if amount < 1 then
> Err InvalidAmount
> else if planet.owner /= team.no
> then
> Err NotYourPlanet
> else if planet.resources < amount
> * defenseCost then
> Err NotEnoughResources
> else
> planet
> |> addDefense amount
> |> subtractResources
> (amount * defenseCost)
> |> updatePlanet star
> |> updateStar game
> |> Ok
> )
> )
> )
>
> How am I supposed to write this "the Elm way"?
>
What I would try to do different is split buildDefense in two between
finding the proper planet and doing something with it
findPlanet : TeamNo -> StarNo -> PlanetNo -> Game -> Result Error ( Planet,
Star )
findPlanet teamNo starNo planetNo game =
findTeamR teamNo game
|> Result.andThen
(\team ->
findStarR starNo game
|> Result.andThen
(\star ->
findPlanetR planetNo star
|> Result.andThen
(\planet ->
if planet.owner /= team.no then
Err NotYourPlanet
else
Ok ( planet, star )
)
)
)
buildDefense : TeamNo -> StarNo -> PlanetNo -> Int -> Game -> Result Error
Game
buildDefense teamNo starNo planetNo amount game =
let
planetAndStar =
findPlanet teamNo starNo planetNo
updateGame ( planet, star ) =
if amount < 1 then
Err InvalidAmount
else if planet.resources < amount * defenseCost then
Err NotEnoughResources
else
planet
|> addDefense amount
|> subtractResources (amount * defenseCost)
|> updatePlanet star
|> updateStar game
|> Ok
in
Result.andThen updateGame planetAndStar
--
There is NO FATE, we are the creators.
blog: http://damoc.ro/
--
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 [email protected].
For more options, visit https://groups.google.com/d/optout.