Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

subworlds #1353

Merged
merged 25 commits into from
Jul 22, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
address sequenceTuple issue
  • Loading branch information
kostmo committed Jul 20, 2023
commit a6f0f4583d4ba14a3ca7f5ef0ffe7b8f509049ae
38 changes: 36 additions & 2 deletions src/Swarm/Game/Scenario/Topography/Navigation/Portal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import Linear (V2, negated)
import Swarm.Game.Location
import Swarm.Game.Scenario.Topography.Navigation.Waypoint
import Swarm.Game.Universe
import Swarm.Util (allEqual, binTuples, both, failT, quote, sequenceTuple)
import Swarm.Util (allEqual, binTuples, both, failT, quote)

type WaypointMap = M.Map WaypointName (NonEmpty Location)

Expand Down Expand Up @@ -249,10 +249,44 @@ ensureSpatialConsistency xs =

groupedBySubworldPair ::
Map (SubworldName, SubworldName) (NonEmpty (Signed (Location, Location)))
groupedBySubworldPair = binTuples $ map (sequenceTuple . fmap tuplify) normalizedOrdering
groupedBySubworldPair = binTuples $ map (sequenceSigned . fmap tuplify) normalizedOrdering

vectorized :: Map (SubworldName, SubworldName) (NonEmpty (V2 Int32))
vectorized = M.map (NE.map (getSigned . fmap (uncurry (.-.)))) groupedBySubworldPair

nonUniform :: Map (SubworldName, SubworldName) (NonEmpty (V2 Int32))
nonUniform = M.filter ((not . allEqual) . NE.toList) vectorized

-- |
-- An implementation of 'sequenceA' for 'Signed' that does not
-- require an 'Applicative' instance for the inner 'Functor'.
--
-- == Discussion
-- Compare to the 'Traversable' instance of 'Signed':
-- @
-- instance Traversable Signed where
-- traverse f (Positive x) = Positive <$> f x
-- traverse f (Negative x) = Negative <$> f x
-- @
--
-- if we were to substitute 'id' for f:
-- @
-- traverse id (Positive x) = Positive <$> id x
-- traverse id (Negative x) = Negative <$> id x
-- @
-- our implementation essentially becomes @traverse id@.
--
-- However, we cannot simply write our implementation as @traverse id@, because
-- the 'traverse' function has an 'Applicative' constraint, which is superfluous
-- for our purpose.
--
-- Perhaps there is an opportunity to invent a typeclass for datatypes which
-- consist exclusively of unary (or more ambitiously, non-nullary?) data constructors,
-- for which a less-constrained 'sequence' function could be automatically derived.
sequenceSigned ::
Functor f =>
Signed (f a) ->
f (Signed a)
sequenceSigned = \case
Positive x -> Positive <$> x
Negative x -> Negative <$> x
17 changes: 0 additions & 17 deletions src/Swarm/Util.hs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ module Swarm.Util (
findDup,
both,
allEqual,
sequenceTuple,

-- * Directory utilities
readFileMay,
Expand Down Expand Up @@ -195,22 +194,6 @@ allEqual :: (Ord a) => [a] -> Bool
allEqual [] = True
allEqual (x : xs) = all (== x) xs

-- | This function has a lamentable basis.
-- The 'sequenceA' function requires an 'Applicative' instance
-- for the inner 'Functor'. However, the 'Applicative' instance
-- of @(,)@ (the two-element tuple) requires a 'Monoid' instance
-- for the first element!
-- See: https://hackage.haskell.org/package/base-4.18.0.0/docs/src/GHC.Base.html#line-523
--
-- The 'sequenceA' operation does not affect the first element
-- of the tuple, so it shouldn't matter whether it has a 'Monoid' instance!
-- To satisfy the compiler, we abuse a list to first wrap and then unwrap after a traversal.
sequenceTuple ::
Traversable f =>
f (a, b) ->
(a, f b)
sequenceTuple = first head . traverse (first pure)

------------------------------------------------------------
-- Directory stuff

Expand Down