ck :: (MonadError String m, Ord channel, Show channel) =>
         Maybe channel -> [channel] -> EndoM m (Skel channel)
check c cs = fmap (uncurry (<>)) . go where
  scs = l2s cs
  -- Given a skeleton sk, `go` returns a pair (sk', sk'')
  -- sk'  is a skeleton with    occurrences from scs
  -- sk'' is a skeleton without occurrences from scs
  -- sk should be equivalent to sk' | sk''
  go = \case
    Zero -> return (Zero, Zero)
    Act a
      | a `member` scs -> return (Act a, Zero)
      | otherwise      -> return (Zero,  Act a)
    Op op sk0 sk1 -> do
      (sk0', sk0'') <- go sk0
      (sk1', sk1'') <- go sk1
      let
        cs0 = scs `intersection` fcSkel sk0'
        cs1 = scs `intersection` fcSkel sk1'
        cs' = cs0 `union` cs1
      -- We could throw an error when the intersection of cs0 and cs1
      -- is not null, however these errors are also caught elsewhere
      -- (when merging parallel protocols for instance)
      -- One case we want to let pass is when the same channel is used
      -- twice on one side:
      -- c[d,e] (send d 1 | send e 2. send a 3. send e 4)
      -- Here the skeleton is: `(d | e.a.e)`, the channel `e` appear twice
      -- and we chose to keep the ordering. We need the reconstruction of:
      -- `e.a.e` not to raise an error.
      case op of
        _   | null cs0 && null cs1 ->
          return (Zero, mkOp op (sk0' <> sk0'') (sk1' <> sk1''))

        Prll True | not (null cs1) && not (null cs0) ->
          return (prllFalse c cs' sk0' sk1', sk0'' <> sk1'')
        Prll True | null cs0 && not (null cs1) ->
          return (sk1', sk0' <> sk0'' <> sk1'')
        Prll True | null cs1 && not (null cs0) ->
          return (sk0', sk0'' <> sk1' <> sk1'')

        _ | not (null cs1) && not (null cs0) && cs0 /= cs1 ->
          throwError $ "checkSel: " ++ show op

        _ ->
          return (mkOp op (sk0' <> sk0'') (sk1' <> sk1''), Zero)

check :: (MonadError String m, Ord channel, Show channel) =>
         Maybe channel -> [channel] -> EndoM m (Skel channel)
check c cs = fmap (uncurry (<>)) . go where
  scs = l2s cs
  -- Given a skeleton sk, `go` returns a pair (sk', sk'')
  -- sk'  is a skeleton with    occurrences from scs
  -- sk'' is a skeleton without occurrences from scs
  -- sk should be equivalent to sk' | sk''
  go = \case
    Zero -> return (Zero, Zero)
    Act a
      | a `member` scs -> return (Act a, Zero)
      | otherwise      -> return (Zero,  Act a)
    Op op sk0 sk1 -> do
      (sk0', sk0'') <- go sk0
      (sk1', sk1'') <- go sk1
      let
        cs0 = scs `intersection` fcSkel sk0'
        cs1 = scs `intersection` fcSkel sk1'
        cs' = cs0 `union` cs1
      -- We could throw an error when the intersection of cs0 and cs1
      -- is not null, however these errors are also caught elsewhere
      -- (when merging parallel protocols for instance)
      -- One case we want to let pass is when the same channel is used
      -- twice on one side:
      -- c[d,e] (send d 1 | send e 2. send a 3. send e 4)
      -- Here the skeleton is: `(d | e.a.e)`, the channel `e` appear twice
      -- and we chose to keep the ordering. We need the reconstruction of:
      -- `e.a.e` not to raise an error.
      case op of
        _   | null cs0 && null cs1 ->
          return (Zero, mkOp op (sk0' <> sk0'') (sk1' <> sk1''))

        Prll True | not (null cs1) && not (null cs0) ->
          return (prllFalse c cs' sk0' sk1', sk0'' <> sk1'')
        Prll True | null cs0 && not (null cs1) ->
          return (sk1', sk0' <> sk0'' <> sk1'')
        Prll True | null cs1 && not (null cs0) ->
          return (sk0', sk0'' <> sk1' <> sk1'')

        _ | not (null cs1) && not (null cs0) && cs0 /= cs1 ->
          throwError $ "checkSel: " ++ show op

        _ ->
          return (mkOp op (sk0' <> sk0'') (sk1' <> sk1''), Zero)
check c cs = fmap (uncurry (<>)) . go where
  scs = l2s cs
  -- Given a skeleton sk, `go` returns a pair (sk', sk'')
  -- sk'  is a skeleton with    occurrences from scs
  -- sk'' is a skeleton without occurrences from scs
  -- sk should be equivalent to sk' | sk''
  go = \case
    Zero -> return (Zero, Zero)
    Act a
      | a `member` scs -> return (Act a, Zero)
      | otherwise      -> return (Zero,  Act a)
    Op op sk0 sk1 -> do
      (sk0', sk0'') <- go sk0
      (sk1', sk1'') <- go sk1
      let
        cs0 = scs `intersection` fcSkel sk0'
        cs1 = scs `intersection` fcSkel sk1'
        cs' = cs0 `union` cs1
      -- We could throw an error when the intersection of cs0 and cs1
      -- is not null, however these errors are also caught elsewhere
      -- (when merging parallel protocols for instance)
      -- One case we want to let pass is when the same channel is used
      -- twice on one side:
      -- c[d,e] (send d 1 | send e 2. send a 3. send e 4)
      -- Here the skeleton is: `(d | e.a.e)`, the channel `e` appear twice
      -- and we chose to keep the ordering. We need the reconstruction of:
      -- `e.a.e` not to raise an error.
      case op of
        _   | null cs0 && null cs1 ->
          return (Zero, mkOp op (sk0' <> sk0'') (sk1' <> sk1''))

        Prll True | not (null cs1) && not (null cs0) ->
          return (prllFalse c cs' sk0' sk1', sk0'' <> sk1'')
        Prll True | null cs0 && not (null cs1) ->
          return (sk1', sk0' <> sk0'' <> sk1'')
        Prll True | null cs1 && not (null cs0) ->
          return (sk0', sk0'' <> sk1' <> sk1'')

        _ | not (null cs1) && not (null cs0) && cs0 /= cs1 ->
          throwError $ "checkSel: " ++ show op

        _ ->
          return (mkOp op (sk0' <> sk0'') (sk1' <> sk1''), Zero)