well-typed / generics-sop

Generic Programming using True Sums of Products
BSD 3-Clause "New" or "Revised" License
157 stars 48 forks source link

Zip NP and NS to produce NP #11

Closed UnkindPartition closed 7 years ago

UnkindPartition commented 9 years ago

Here's an alternative generalization of <*>:

hap' :: forall f (xs :: [k]) . NS (f -.-> f) xs -> NP f xs -> NP f xs
hap' (Z (Fn f)) (h :* t) = f h :* t
hap' (S f) (h :* t) = h :* hap' f t
hap' _ _ = error "impossible"

Unless I'm missing something, it can't be implemented in terms of the existing combinators. If so, I suggest adding it under some name.

kosmikus commented 9 years ago

Interesting. I'm willing to add it, yes. Just struggling with naming. Perhaps something like this:

expand_NS :: forall xs f . SingI xs => NS (f -.-> f) xs -> NP (f -.-> f) xs
expand_NS ns = case sing :: Sing xs of
  SCons -> case ns of
    Z f   -> f     :* hpure (Fn id)
    S ns' -> Fn id :* expand_NS ns'

expand_SOP :: forall xss f . SingI xss => SOP (f -.-> f) xss -> POP (f -.-> f) xss
expand_SOP (SOP sop) = case sing :: Sing xss of
  SCons -> case sop of
    Z np   -> POP (np :* unPOP (hpure (Fn id)))
    S sop' -> POP (hpure (Fn id) :* unPOP (expand_SOP (SOP sop')))

class (Prod (Prod h) ~ Prod h) => Expandable (h :: (k -> *) -> (l -> *)) where
  hexpand :: SingI xs => h (f -.-> f) xs -> Prod h (f -.-> f) xs

instance Expandable NS where
  hexpand = expand_NS

instance Expandable SOP where
  hexpand = expand_SOP

mapSingle :: (SingI xs, Expandable h, HAp (Prod h)) => h (f -.-> f) xs -> Prod h f xs -> Prod h f xs
mapSingle ns np = hexpand ns `hap` np
UnkindPartition commented 9 years ago

Looks good.

kosmikus commented 9 years ago

Sorry, but I'm going to not include this in 0.2 just yet. I'm still planning to do this, though. I'm just still thinking about the <*> stuff for NS/NP combinations, and may move away from the name hap for these, as I think it's actually confusing.

UnkindPartition commented 9 years ago

Ok, no problem.

kosmikus commented 7 years ago

@feuerbach Just looked at this again due to the mention in https://ro-che.info/articles/2015-07-26-better-yaml-parsing

I think that

hap' = flip (hzipWith (flip apFn))
kosmikus commented 7 years ago

@feuerbach Ah no, my mistake. The result type is wrong.

phadej commented 7 years ago

Are we looking for Defaultable1 f => NS f xs -> NP f xs function here, I don't think it make sense to restrict ourselves to g -.-> g function type, but I'm not sure wha's the right type-class for f would be.

phadej commented 7 years ago

alternatively hset :: NS f xs -> NP f xs -> NP f xs would make sense, but then it generalises to the original hap'

hApplyInj :: (forall a. f a -> g a -> g a) -> NS f xs -> NP g xs -> NP g xs
adamConnerSax commented 7 years ago

Not sure this is the same thing at all but I have recently found use (in https://github.com/adamConnerSax/perConstructor-sop ) for:

 expand::forall (f :: [k] -> *) xs.(SListI xs)=>NS f xs -> NP (Maybe :.: f) xs

 expand ns = go sList (Just ns) where

   go::forall ys.SListI ys => SList ys -> Maybe (NS f ys) -> NP (Maybe :.: f) ys

   go SNil _ = Nil

   go SCons mNS = case mNS of

      Nothing -> Comp Nothing :* go sList Nothing -- after Z

      Just ms -> case ms of

        Z fx -> Comp (Just fx) :* go sList Nothing -- at Z

        S ms' -> Comp Nothing :* go sList (Just ms') -- before Z

which seems like it might of the same ilk, though not quite. I see why "Defaultable1 f" (I don't know what that is and googling is not helping but I can guess and it makes sense.) would be useful. Though the version I had use for was general in the f but required something like Maybe to compose with. Though perhaps (Maybe :.: f) is Defaultable1 (?) and then my case is essentially covered?

I'm a beginner with generics-sop (and markdown!), so forgive me if this is not a useful contribution to the discussion.

Adam

kosmikus commented 7 years ago

@phadej I think generalizing past the function type makes sense. We could get around the need for an additional type class by explicitly passing in the default. Something like

expand_NS :: SListI xs => (forall x . f x) -> NS f xs -> NP f xs
cexpand_NS :: All c xs => proxy c -> (forall x . c x => f x) -> NS f xs -> NP f xs

(and similar functions for SOP and POP).

Then AFAICS, the function @adamConnerSax needs is just

expand :: SListI xs => NS f xs -> NP (Maybe :.: f) xs
expand = expand_NS (Comp Nothing) . hmap (Comp . Just)
kosmikus commented 7 years ago

After all this time, I've actually implemented this and made a PR (#33). Feedback welcome.

kosmikus commented 7 years ago

@feuerbach I hope that the expand functions as now merged via #33 are useful, and that a separate hap variant is then not needed anymore.

UnkindPartition commented 7 years ago

Yes, now the code is much simpler. Thanks!