Skip to content

Instantly share code, notes, and snippets.

@sebfisch
Last active December 14, 2015 08:48
Show Gist options
  • Select an option

  • Save sebfisch/5060428 to your computer and use it in GitHub Desktop.

Select an option

Save sebfisch/5060428 to your computer and use it in GitHub Desktop.

Revisions

  1. Sebastian Fischer revised this gist Mar 1, 2013. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions Mappers.hs
    Original file line number Diff line number Diff line change
    @@ -126,4 +126,5 @@ We can reason as follows to simplify combined mappers:
    (glob "*.scala" "*.hs" >>> glob "Mapper.*" "Mappers.*")
    =
    (identity &&& glob "*.scala" "*.hs") >>> glob "Mapper.*" "Mappers.*"
    -}
  2. Sebastian Fischer revised this gist Mar 1, 2013. 1 changed file with 16 additions and 14 deletions.
    30 changes: 16 additions & 14 deletions Mappers.hs
    Original file line number Diff line number Diff line change
    @@ -27,9 +27,8 @@ type Mapper = String -> [String]

    {-
    Mapper is a semiring.
    `&&&` is addition, `discard` is its unit.
    The combinator `&&&` composes two mappers by collecting their results in a
    list. `discard` is its unit.
    discard &&& m = m
    m &&& discard = m
    @@ -47,7 +46,8 @@ discard _ = []

    {-
    `>>>` is multiplication, `identity` is its unit.
    The combinator `>>>` chains two mappers by applying the second mapper to every
    result of the first and collecting all results. `identity` is its unit.
    identity >>> m = m
    m >>> identity = m
    @@ -65,7 +65,7 @@ l >>> r = concatMap r . l

    {-
    Addition and multiplication are associative.
    Both `&&&` and `>>>` are associative operators.
    a &&& (b &&& c) = (a &&& b) &&& c
    a >>> (b >>> c) = (a >>> b) >>> c
    @@ -86,13 +86,16 @@ chained = foldr (>>>) identity
    `discard` is a zero of `>>>`.
    discard >>> m = discard
    m >>> discrad = discard
    m >>> discard = discard
    Multiplication distributes over addition.
    Composition distributes over chaining from the right.
    a >>> (b &&& c) = (a >>> b) &&& (a >>> c)
    (a &&& b) >>> c = (a >>> c) &&& (b >>> c)
    It distributes from the left, if we ignore the order of computed results.
    sort . (a >>> (b &&& c)) = sort . ((a >>> b) &&& (a >>> c))
    -}


    @@ -116,12 +119,11 @@ glob fromPat toPat =
    We can reason as follows to simplify combined mappers:
    glob "*.java" "*.scala" &&&
    (glob "*.java" "*.scala" >>> glob "*.scala" "*.hs")
    glob "Mapper.*" "Mappers.*" &&&
    (glob "*.scala" "*.hs" >>> glob "Mapper.*" "Mappers.*")
    =
    (glob "*.java" "*.scala" >>> identity) &&&
    (glob "*.java" "*.scala" >>> glob "*.scala" "*.hs")
    (identity >>> glob "Mapper.*" "Mappers.*") &&&
    (glob "*.scala" "*.hs" >>> glob "Mapper.*" "Mappers.*")
    =
    glob "*.java" "*.scala" >>> (identity &&& glob "*.scala" "*.hs")
    (identity &&& glob "*.scala" "*.hs") >>> glob "Mapper.*" "Mappers.*"
    -}
  3. Sebastian Fischer revised this gist Feb 28, 2013. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion Mappers.hs
    Original file line number Diff line number Diff line change
    @@ -122,6 +122,6 @@ We can reason as follows to simplify combined mappers:
    (glob "*.java" "*.scala" >>> identity) &&&
    (glob "*.java" "*.scala" >>> glob "*.scala" "*.hs")
    =
    (glob "*.java" "*.scala" >>> (identity &&& glob "*.scala" "*.hs"))
    glob "*.java" "*.scala" >>> (identity &&& glob "*.scala" "*.hs")
    -}
  4. Sebastian Fischer revised this gist Feb 28, 2013. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion Mappers.hs
    Original file line number Diff line number Diff line change
    @@ -114,7 +114,7 @@ glob fromPat toPat =

    {-
    Now, we can reason as follows to simplify combined mappers:
    We can reason as follows to simplify combined mappers:
    glob "*.java" "*.scala" &&&
    (glob "*.java" "*.scala" >>> glob "*.scala" "*.hs")
  5. Sebastian Fischer revised this gist Feb 28, 2013. 1 changed file with 16 additions and 0 deletions.
    16 changes: 16 additions & 0 deletions Mappers.hs
    Original file line number Diff line number Diff line change
    @@ -15,6 +15,7 @@ considering their implementation.
    -}


    module Mappers where


    @@ -109,3 +110,18 @@ glob fromPat toPat =
    case elemIndices '*' s of
    [] -> (s,"")
    ps -> (take (last ps) s, drop (last ps+1) s)


    {-
    Now, we can reason as follows to simplify combined mappers:
    glob "*.java" "*.scala" &&&
    (glob "*.java" "*.scala" >>> glob "*.scala" "*.hs")
    =
    (glob "*.java" "*.scala" >>> identity) &&&
    (glob "*.java" "*.scala" >>> glob "*.scala" "*.hs")
    =
    (glob "*.java" "*.scala" >>> (identity &&& glob "*.scala" "*.hs"))
    -}
  6. Sebastian Fischer created this gist Feb 28, 2013.
    111 changes: 111 additions & 0 deletions Mappers.hs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,111 @@
    {-
    A German blog post translating Java code for the build tool Ant to Scala
    http://funktionale-programmierung.de/2013/02/26/scala-java-ant.html
    made me translate the Scala code to Haskell. I added combinators for binary
    composition of mappers as well as a unit mapper for one of them. The code is
    still shorter. I find the Haskell version more readable because expressions
    and their types are separate rather than interleaved as in Scala.
    Defining binary operators where the Scala version only had variants on lists
    reveals algebraic properties that can be used to reason about mappers without
    considering their implementation.
    -}

    module Mappers where


    import Data.List (elemIndices)


    type Mapper = String -> [String]


    {-
    Mapper is a semiring.
    `&&&` is addition, `discard` is its unit.
    discard &&& m = m
    m &&& discard = m
    -}


    discard :: Mapper
    discard _ = []


    (&&&) :: Mapper -> Mapper -> Mapper
    (l &&& r) s = l s ++ r s


    {-
    `>>>` is multiplication, `identity` is its unit.
    identity >>> m = m
    m >>> identity = m
    -}


    identity :: Mapper
    identity s = [s]


    (>>>) :: Mapper -> Mapper -> Mapper
    l >>> r = concatMap r . l


    {-
    Addition and multiplication are associative.
    a &&& (b &&& c) = (a &&& b) &&& c
    a >>> (b >>> c) = (a >>> b) >>> c
    -}


    composite :: [Mapper] -> Mapper
    composite = foldr (&&&) discard


    chained :: [Mapper] -> Mapper
    chained = foldr (>>>) identity


    {-
    `discard` is a zero of `>>>`.
    discard >>> m = discard
    m >>> discrad = discard
    Multiplication distributes over addition.
    a >>> (b &&& c) = (a >>> b) &&& (a >>> c)
    (a &&& b) >>> c = (a >>> c) &&& (b >>> c)
    -}


    glob :: String -> String -> Mapper
    glob fromPat toPat =
    \s -> [ toPre ++ mid ++ toPost
    | let (pre, rest) = splitAt (length fromPre) s
    (mid, post) = splitAt (length rest - length fromPost) rest,
    pre == fromPre, post == fromPost ]
    where
    (fromPre, fromPost) = splitAtLastStar fromPat
    (toPre, toPost) = splitAtLastStar toPat

    splitAtLastStar s =
    case elemIndices '*' s of
    [] -> (s,"")
    ps -> (take (last ps) s, drop (last ps+1) s)