2017-11-16 11 views
3

몇 가지 경우에의 변환을 실행 :다형성 구조를 순회 만한다고 가정 우리는 다음과 같은 방법으로 회사의 계층 구조를 나타냅니다

{-# LANGUAGE DeriveDataTypeable #-} 

import   Data.Data 
import   Data.Generics.Aliases 
import   Data.Generics.Schemes 

data CompanyAsset = Employee Name Salary 
        | Plant Name 
        | Boss Name Performance Salary [CompanyAsset] 
        | Pet Name 
        | Car Id 
        | Guild [CompanyAsset] 
        | Fork CompanyAsset CompanyAsset 
        -- ... and imagine 100 more options that recursively use `CompanyAsset`. 
        deriving (Show, Data) 

-- Performance of the department. 
data Performance = Good | Bad deriving (Show, Data) 

type Name = String 

type Id = Int 

newtype Salary = Salary Double deriving (Show, Data, Typeable) 

raise :: Salary -> Salary 

그리고 난 몰라 회사 자산의 급여를 제기하는 기능을 defne 싶습니다 해당 부서의 실적이 BadBoss 조상을 보유하고 있습니다. 다음과 같은 기능을 쉽게 정의 할 수 있습니다 :

raiseSalaries :: CompanyAsset -> CompanyAsset 
raiseSalaries (Boss n Good s as) = Boss n Good (raise s) (raiseSalaries <$> as) 
raiseSalaries [email protected](Boss _ Bad _ _) = a -- The salaries of everything below are not raised if the performance is 'Bad' 
raiseSalaries ... -- and from here onwards we have **boilerplate**! 

문제는이 상용구를 많이 필요로한다는 것이다 (토론을 위해서가의 CompanyAsset이 주어 변경 될 수 있다고 가정하십시오).

제 질문은 위의 상용구를 피할 수있는 방식으로 데이터 구조를 탐색하는 방법이 있는지 여부입니다.

이 질문은 내가 게시 한 similar one과 관련이 있지만, everywhere'의 사용은 급여를 인상해서는 안되기 때문에 도움이되지 않습니다.

답변

2

이것은 CompanyAsset에 대해 Traversal으로 수행 할 수 있습니다. 직접 쓰거나 렌즈에서 uniplate 또는 plate을 사용하십시오.

설명을 위해, 나는 CompanyAsset에 대한 순회를 명시 적으로 작성하려고합니다. 그것은 회사 자산의 각 직계 종속물에 작업 (pure과 같이 p이라고 부름)을 적용합니다. traverse_ca pure == pure에 유의하십시오.

traverse_ca :: Applicative f => (CompanyAsset -> f CompanyAsset) -> CompanyAsset -> f CompanyAsset 
traverse_ca p ca = 
    case ca of 
    Fork ca1 ca2  -> Fork <$> p ca1 <*> p ca2 
    Boss n perf s cas -> Boss n perf s <$> traverse p cas 
    Guild cas   -> Guild <$> traverse p cas 
    otherwise   -> pure ca 

는 자체로이 추가 상용구없이 raiseSalaries를 정의하기에 충분하다.

import Data.Functor.Identity 

raiseSalaries :: CompanyAsset -> CompanyAsset 
raiseSalaries (Boss n Good s as) = Boss n Good (raise s) (raiseSalaries <$> as) 
raiseSalaries [email protected](Boss _ Bad _ _) = a -- The salaries of everything below are not raised if the performance is 'Bad' 
raiseSalaries a = runIdentity $ traverse_ca (pure . raiseSalaries) a 
1

염기를 CompanyAssetF 펑를 생성하는 것도 a bit of Template Haskellrecursion-schemes를 사용하고, 용액 :

{-# LANGUAGE TemplateHaskell #-} 
{-# LANGUAGE DeriveFunctor #-} 
{-# LANGUAGE DeriveFoldable #-} 
{-# LANGUAGE DeriveTraversable #-} 

import Data.Functor.Foldable (cata,embed) 
import Data.Functor.Foldable.TH (makeBaseFunctor) 

$(makeBaseFunctor ''CompanyAsset) 

raiseSalaries :: CompanyAsset -> CompanyAsset 
raiseSalaries asset = cata go asset raise' 
    where 
    go c raiser = embed $ 
     case c of 
      BossF _ Bad _ _ -> fmap ($ id) c 
      _ -> raiser $ fmap ($ raiser) c 
    raise' (BossF name perf salary rec) = BossF name perf (raise salary) rec 
    raise' (EmployeeF name salary) = EmployeeF name (raise salary) 
    raise' other = other 
대수가 가능하게하기 위해 함수를 리턴

상기에서 정보 흐름을 "상승 받아야" 잎 뿌리.