1

I have finally found an excellent entry point into functional programming with elm, and boy, do I like it, yet I still lack some probably fundamental elegance concerning a few concepts.

I often find myself writing code similar to the one below, which seems to be doing what it should, but if someone more experienced could suggest a more compact and direct approach, I am sure that could give some valuable insights into this so(u)rcery.

What I imagine this could boil down to, is something like the following (<-> is a vector subtraction operator):

edgeDirections : List Vector -> List Vector
edgeDirections corners = List.map2 (\p v -> p <-> v) corners (shiftr 1 corners)

but I don't really have a satisfying approach to a method that would do a shiftr.

But the rules of stackoverflow demand it, here is what I tried. I wrote an ugly example of a possible usage for shiftr (I absolutely dislike the Debug.crash and I am not happy about the Maybe):

Given a list of vectors (the corner points of a polygon), calculate the directional vectors by calculating the difference of each corner-vector to its previous one, starting with the diff between the first and the last entry in the list.

[v1,v2,v3] -> [v1-v3,v2-v1,v3-v2]

Here goes:

edgeDir : Vector -> ( Maybe Vector, List Vector ) -> ( Maybe Vector, List Vector )
edgeDir p ( v, list ) =
  case v of
    Nothing ->
      Debug.crash ("nono")

    Just vector ->
      ( Just p, list ++ [ p <-> vector ] )


edgeDirections : List Vector -> List Vector
edgeDirections corners =
  let
    last =
      List.head <| List.reverse corners
  in
    snd <| List.foldl edgeDir ( last, [] ) corners


main =
  show <| edgeDirections [ Vector -1 0, Vector 0 1, Vector 1 0 ]

I appreciate any insight into how this result could be achieved in a more direct manner, maybe using existing language constructs I am not aware of yet, or any pointers on how to lessen the pain with Maybe. The latter may Just not be possible, but I am certain that the former will a) blow me away and b) make me scratch my head a couple times :)

Thank you, and many thanks for this felicitous language!

mindandmedia
  • 6,800
  • 1
  • 24
  • 33

2 Answers2

2

If Elm had built-in init and last functions, this could be cleaner.

You can get away from all those Maybes by doing some pattern matching. Here's my attempt using just pattern matching and an accumulator.

import List exposing (map2, append, reverse)

shiftr list = 
  let shiftr' acc rest =
    case rest of
      [] -> []
      [x] -> x :: reverse acc
      (x::xs) -> shiftr' (x::acc) xs
  in shiftr' [] list

edgeDirections vectors =
  map2 (<->) vectors <| shiftr vectors

Notice also the shortened writing of the mapping function of (<->), which is equivalent to (\p v -> p <-> v).

Suppose Elm did have an init and last function - let's just define those quickly here:

init list =
  case list of
    [] -> Nothing
    [_] -> Just []
    (x::xs) -> Maybe.map ((::) x) <| init xs

last list =
  case list of
    [] -> Nothing
    [x] -> Just x
    (_::xs) -> last xs

Then your shiftr function could be shortened to something like:

shiftr list =
  case (init list, last list) of
    (Just i, Just l) -> l :: i
    _ -> list
Chad Gilbert
  • 36,115
  • 4
  • 89
  • 97
  • This is exactly the pattern I had seen previously and thought it could be applicable here. Thank you for the demonstration! Just out of curiousity, does `(x::xs)` imply, that `x` is not `Nothing`? Edit: strike that, I am sure it does not imply that, that's why this is advantageous, got it. – mindandmedia Apr 07 '16 at 20:38
0

Just after I "hung up", I came up with this, but I am sure this can still be greatly improved upon, if it's even correct (and it only works for n=1)

shiftr : List a -> List a
shiftr list =
  let
    rev =
      List.reverse list
  in
    case List.head rev of
      Nothing ->
        list

      Just t ->
        [ t ] ++ (List.reverse <| List.drop 1 rev)


main =
  show (shiftr [ 1, 2, 3, 4 ] |> shiftr)
mindandmedia
  • 6,800
  • 1
  • 24
  • 33