Why generic function process list head in a special way?

Viewed 90

I am developing a generic function to remove sub value from a complex value according to some criteria. Here I remove values with data constructor containing "z" letter. It is almost working as I want.

> genericFilter (1,[Yez, No])
Just (1,[No])

But there is a special case when the whole list is dropped if Yez is the first item on the list.

genericFilter (1,[[Yez, No]])
Just (1,[])
>genericFilter [Yez, No, No]
Nothing

After debugging I noticed that the issue in :*:. For the first argument of :*: FilterZ (SomeZ) instance is used directly bypassing FilterZ MetaConst and Filter (K1 []), mean while for the rest list FilterZ MetaConst and Filter (K1 []) are used and FilterZ (SomeZ) is not!

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ScopedTypeVariables #-}


module Main where

import Data.Proxy
import GHC.Generics
import GHC.TypeLits
import Data.List
data SomeZ = No | Yez deriving (Show, Eq, Generic)

class FilterZ a where
  gfilter :: a x -> Maybe (a x)

instance FilterZ (U1) where
  gfilter U1 = Just U1

instance FilterZ (V1) where -- void
  gfilter _ = Nothing

instance  FilterZ (K1 _1 ()) where
   gfilter (K1 ()) = Just $ K1 ()

instance  FilterZ (K1 _1 SomeZ) where
   gfilter (K1 No) = Just $ K1  No
   gfilter (K1 Yez) = Nothing -- Just $ K1 Yez -- Nothing


instance (FilterZ (Rep a), Show a, Generic a) => FilterZ (K1 _1 [a]) where
   gfilter (K1 []) = Just $ K1 []
   gfilter (K1 (h:r)) = case gfilter (from h) of
                          Nothing -> gfilter (K1 r)
                          Just h' -> case gfilter (K1 r) of
                                       Nothing -> Just $ K1 [(to h') :: a] -- Nothing
                                       Just (K1 r') -> Just $ K1 ((to h') : r')

instance FilterZ (K1 _1 Int) where
   gfilter (K1 n) = Just $ K1 n

instance FilterZ (K1 _1 Integer) where
   gfilter (K1 n) = Just $ K1 n


instance (FilterZ a, FilterZ b) => FilterZ (a :+: b) where
  gfilter (L1 x) = case gfilter x of
                     Nothing -> Nothing
                     Just x' -> Just $ L1 x'

  gfilter (R1 x) = case gfilter x of
                     Nothing -> Nothing
                     Just x' -> Just $ R1 x'

instance (FilterZ a, FilterZ b) => FilterZ (a :*: b) where
  gfilter (a :*: b) =
    case gfilter a of
      Nothing -> Nothing
      Just a' -> case gfilter b of
                   Nothing -> Nothing
                   Just b' -> Just $ a' :*: b'

instance FilterZ c => FilterZ (M1 a ('MetaData dname mname pname isnewtype) c) where
   gfilter (M1 x) = case gfilter x of
                      Nothing -> Nothing
                      Just x' -> Just $ M1 x'

instance (KnownSymbol dcn, FilterZ c) => FilterZ (M1 a ('MetaCons dcn p f) c) where
   gfilter (M1 x) = case find (=='z') name of
                      Just _  -> Nothing
                      Nothing -> case gfilter x of
                                   Nothing -> Nothing
                                   Just x' -> Just $ M1 x'
     where
       name = symbolVal (undefined :: Proxy dcn)

instance FilterZ c => FilterZ (M1 a ('MetaSel fsel packness stricnesss lazines) c) where
   gfilter (M1 x) = case gfilter x of
                      Nothing -> Nothing
                      Just x' -> Just $ M1 x'

genericFilter :: (Generic a, FilterZ (Rep a)) => a -> Maybe a
genericFilter a = fmap to $ gfilter (from a)

1 Answers

The function from works at the top level only. So if you apply from to a list you get its generic representation which is either unit or product of its head and its tail:

*Gen> from [Yez, No]
M1 {unM1 = R1 (M1 {unM1 = M1 {unM1 = K1 {unK1 = Yez}} :*: M1 {unM1 = K1 {unK1 = [No]}}})}

Note that the head is separated but [No] is not decomposed any further. So if your list not at the top level it is never decomposed like this under from:

*Gen> from (1, [Yez, No])
M1 {unM1 = M1 {unM1 = M1 {unM1 = K1 {unK1 = 1}} :*: M1 {unM1 = K1 {unK1 = [Yez,No]}}}}

Note the list [Yez, No] is kept intact.

In the first case genericFilter passes through M1 and reaches a :*:. The first component of the product is Yez, so by the FilterZ (K1 _1 SomeZ) instance it is mapped to Nothing. And the (FilterZ a, FilterZ b) => FilterZ (a :*: b) instance says that the end result should be Nothing.

In the second case again genericFilter passes through M1 and reaches a :*:. This time the first component is a unit, which is mapped to unit and the second component is of type [SomeZ] which is filtered by the (FilterZ (Rep a), Show a, Generic a) => FilterZ (K1 _1 [a]) instance.

Related