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

Why is Foldable a superclass of Crosswalk? #77

Open
cspollard opened this issue Jun 19, 2017 · 3 comments
Open

Why is Foldable a superclass of Crosswalk? #77

cspollard opened this issue Jun 19, 2017 · 3 comments
Assignees

Comments

@cspollard
Copy link

cspollard commented Jun 19, 2017

There is no reference to Foldable in the default definitions, so is there a more fundamental reason?

The reason I ask is because I have a data type that appears to fit the Crosswalk shape, but its Foldable instance is, frankly, not meaningful, and I would prefer not to implement it. However, I wonder if this also makes the Crosswalk instance bogus.

@phadej phadej self-assigned this Jun 19, 2017
@phadej
Copy link
Collaborator

phadej commented Jun 19, 2017

I cannot come with Crosswalkable constainer, which ins't Foldable. #78 is an example (which we cannot have in the lib unfortunately).

@Zemyla
Copy link

Zemyla commented Nov 21, 2018

If we create a free Align, and then specialize its type to Const m, then it's effectively a list of ms, which can be subsequently mappended up. The join law doesn't come up, because a law-abiding Crosswalk can only call fonce for each a inside it. That's why Foldable is a prerequisite, and why foldMapCrosswalk works.

@Zemyla
Copy link

Zemyla commented Dec 14, 2019

I've actually almost proven that every Crosswalk is Traversable, even.

{-# LANGUAGE GADTs #-}

import Data.These
import Data.Align
import Unsafe.Coerce

data FreeAlign f a where
  Nil :: FreeAlign f a
  Lift :: (u -> a) -> f u -> FreeAlign f a
  AlignWith :: (These u v -> a) -> FreeAlign f u -> FreeAlign f v -> FreeAlign f a

instance Functor (FreeAlign f) where
  fmap _ Nil = Nil
  fmap f (Lift g fu) = Lift (f . g) fu
  fmap f (AlignWith g au av) = AlignWith (f . g) au av

instance SemiAlign (FreeAlign f) where
  alignWith = AlignWith

instance Align (FreeAlign f) where
  nil = Nil

-- Try to lower a FreeAlign to an Applicative. The main difference between Align and Applicative is that
-- Align's unit, nil, has no attached value, whereas Applicative's, pure, does. This causes a bit of
-- difficulty.
tryLowerFAA :: Applicative f => FreeAlign f a -> Maybe (f a)
tryLowerFAA Nil = Nothing
tryLowerFAA (Lift g f) = Just (fmap g f)
tryLowerFAA (AlignWith g au av) = case tryLowerFAA au of
  Nothing -> tryLowerFAA (fmap (g . That) av)
  Just fu -> Just $ case tryLowerFAA av of
    Nothing -> fmap (g . This) fu
    Just fv -> liftA2 (\u v -> g (These u v)) fu fv

-- This is almost a proof, but it relies on containers that report being empty to crosswalk actually
-- being empty, and thus the parameter being phantom.
traverseCrosswalk :: (Crosswalk t, Applicative f) => (a -> f b) -> t a -> f (t b)
traverseCrosswalk f t = case tryLowerFAA (crosswalk (Lift id . f) t) of
  Just ft -> ft
  Nothing -> pure (unsafeCoerce t) -- Dubious?

-- By contrast, foldMapCrosswalk works without any issue.
foldMapCrosswalk :: (Monoid m, Crosswalk t) => (a -> m) -> t a -> m
foldMapCrosswalk f t = case tryLowerFAA (crosswalk (Lift id . Const . f) t) of
  Just (Const m) -> m
  Nothing -> mempty

So there we go. Every Crosswalk is also Foldable, and also morally Traversable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants