2014-10-16 3 views
0

FeatureBody을 의미하는 형식 동의어는 type Entity = ([Feature], Body)입니다. Entity 유형의 객체는 함께 그룹화 할 수 있습니다데이터 형식 정의에 대한 제한

type Bunch = [Entity] 

과 가정, Bunch 작업 알고리즘에 대한 중요한, 같은 무리에서 두 기관은 기능의 같은 수를 가지고있다.

만약 OOP 언어로이 제약 조건을 구현한다면, 해당 체크를 묶음으로 엔티티 추가를 캡슐화하는 메소드에 추가 할 것입니다. 하스켈에서 더 좋은 방법이 있습니까? 바람직하게는, 정의 수준에서. (경우 Entity의 정의는, 아무 문제가 변경되지해야합니다.)

+0

'엔터티'의 '기능'수가 컴파일 타임에 알려져 있습니까? 어쨌든, 그것은 [의존형] (https://www.fpcomplete.com/user/konn/prove-your-haskell-for-great-safety/dependent-types-in-haskell)과 비슷하게 들린다. 그 주제에 너무 익숙하지 않아요. – Zeta

답변

2

그래서 여기 거래의 유형 수준의 길이 주석

사용. Haskell에는 type-level natural numbers이 있고 "팬텀 유형"을 사용하는 유형으로 주석을 달 수 있습니다. 당신이 그것을 할 그러나, 종류는 다음과 같이 표시됩니다

data Z 
data S n 
data LAList x len = LAList [x] -- length-annotated list 

는 그런 다음 편의를 위해 일부 건설 기능을 추가 할 수 있습니다

lalist1 :: x -> LAList x (S Z) 
lalist1 x = LAList [x] 
lalist2 :: x -> x -> LAList x (S (S Z)) 
lalist2 x y = LAList [x, y] 
-- ... 

을 그리고 당신이있어 더 일반적인 방법 :

(~:) :: x -> LAList x n -> LAList x (S n) 
x ~: LAList xs = LAList (x : xs) 
infixr 5 ~: 

nil :: LAList x Z 
nil = LAList [] 

lahead :: LAList x (S n) -> x 
lahead (LAList xs) = head xs 

latail :: LAList x (S n) -> LAList x n 
latail (LAList xs) = tail xs 

자체적으로 List 정의에는 이 없기 때문에 복잡합니다. 다소 다른 접근법에 대해서도 Data.FixedList 패키지에 관심이있을 수 있습니다. 기본적으로 모든 접근법은 생성자가없는 일부 데이터 유형으로 약간 이상하게 보일 수도 있지만 조금 지나면 정상으로 보입니다. lalist1의 모든 것을, lalist2 운영자는 위의

class FixedLength t where 
    la :: t x -> LAList x n 

로 대체 할 수 있도록

당신 도 typeclass을 얻을 수있을 수 있지만 당신은 아마,이 작업을 수행 할 -XTypeSynonymInstances 플래그가 필요합니다

type Pair x = (x, x) 
instance FixedLength Pair where 
    la :: Pair x -> LAList [x] (S (S Z)) 
    la (a, b) = LAList [a, b] 

(당신이 (a, b)에서 Pair a에 갈 때 일종의 불일치의)처럼 뭔가를 할 수 있습니다.

-- this may change if you change your definition of the Bunch type 
features :: Entity -> [Feature] 
features = fst 

-- we also assume a runBunch :: [Entity] -> Something function 
-- that you're trying to run on this Bunch. 

allTheSame :: (Eq x) => [x] -> Bool 
allTheSame (x : xs) = all (x ==) xs 
allTheSame [] = True 

permissiveBunch :: [Entity] -> Maybe Something 
permissiveBunch es 
    | allTheSame (map (length . features) es) = Just (runBunch es) 
    | otherwise = Nothing 

strictBunch :: [Entity] -> Something 
strictBunch es 
    | allTheSame (map (length . features) es) = runBunch es 
    | otherwise = error ("runBunch requires all feature lists to be the same length; saw instead " ++ show (map (length . features) es)) 

다음 당신에게 runBunch 그냥 가정 할 수 있습니다 :

당신은 아주 쉽게 다른 접근 방식을 취하고 런타임 오류로이 모든 것을 캡슐화으로 또는 명시 적으로 코드에서 오류를 모델링 할 수 있습니다 확인 런타임을 사용

모든 길이가 같고 위에서 명시 적으로 확인되었습니다. 서로 옆에있는 기능을 쌍으로 연결해야하는 경우, 예를 들어 서막에서 zip :: [a] -> [b] -> [(a, b)] 기능과 함께 패턴 매칭 괴물을 둘러 볼 수 있습니다. (여기서 목표는 runBunch' (x:xs) (y:ys)runBunch' [] []에 대한 패턴 매칭 때문에 알고리즘에서 오류가 될 것이지만, 하스켈은 당신이 매치에서 고려하지 않은 2 가지 패턴이 있다고 경고합니다.) 튜플과 법인이 끝난 매개 변수화 만들기 포함 형 클래스 둘 사이의 타협이다 (하지만 꽤 좋은 하스켈 코드를 만들어 그것을 할 수

마지막으로 방법) 모든 기능을 사용

:

type Entity x = (x, Body) 

class ZippableFeatures z where 
    fzip :: z -> z -> [(Feature, Feature)] 

instance ZippableFeatures() where 
    fzip()() = [] 

instance ZippableFeatures Feature where 
    fzip f1 f2 = [(f1, f2)] 

instance ZippableFeatures (Feature, Feature) where 
    fzip (a1, a2) (b1, b2) = [(a1, b1), (a2, b2)] 

그런 다음 당신은 당신의 기능 목록에 대한 튜플을 사용할 수 있습니다 : 서로 다른 길이의 다른 엔티티를 압축 할 수있는 기능을 포함

다음 , 최대 튜플 길이 (내 GHC에서 15)보다 커지지 않는 한. 물론 그보다 더 커지면 자신 만의 데이터 유형을 정의 할 수는 있지만 유형이 주석으로 지정된 목록처럼 일반적이지는 않습니다.

이렇게하면 runBunch에 대한 유형 서명은 단순히 모양을 : 당신은 당신이 통합 할 수없는 컴파일러 오류를 얻을 수 있습니다 기능의 잘못된 번호 일에 그것을 실행하면

runBunch :: (ZippableFeatures z) => [Entity z] -> Something 

(a, b, c)가있는 유형 (a, b).

+0

아주 좋은 독서. 당신의 노력에 감사드립니다 – AdelNick

2

이와 같은 길이 제한을 적용하는 데는 여러 가지 방법이 있습니다. 여기에서이다 :이 목록에

{-# LANGUAGE DataKinds, KindSignatures, GADTs, TypeFamilies #-} 
import Prelude hiding (foldr) 
import Data.Foldable 
import Data.Monoid 
import Data.Traversable 
import Control.Applicative 

data Feature -- Whatever that really is 

data Body -- Whatever that really is 

data Nat = Z | S Nat -- Natural numbers 

type family Plus (m::Nat) (n::Nat) where -- Type level natural number addition 
    Plus Z n = n 
    Plus (S m) n = S (Plus m n) 

data LList (n :: Nat) a where -- Lists tagged with their length at the type level 
    Nil :: LList Z a 
    Cons :: a -> LList n a -> LList (S n) a 

일부 기능 :

llHead :: LList (S n) a -> a 
llHead (Cons x _) = x 

llTail :: LList (S n) a -> LList n a 
llTail (Cons _ xs) = xs 

llAppend :: LList m a -> LList n a -> LList (Plus m n) a 
llAppend Nil ys = ys 
llAppend (Cons x xs) ys = Cons x (llAppend xs ys) 

data Entity n = Entity (LList n Feature) Body 

data Bunch where 
    Bunch :: [Entity n] -> Bunch 

일부 인스턴스 :

instance Functor (LList n) where 
    fmap f Nil = Nil 
    fmap f (Cons x xs) = Cons (f x) (fmap f xs) 

instance Foldable (LList n) where 
    foldMap f Nil = mempty 
    foldMap f (Cons x xs) = f x `mappend` foldMap f xs 

instance Traversable (LList n) where 
    traverse f Nil = pure Nil 
    traverse f (Cons x xs) = Cons <$> f x <*> traverse f xs 

등등. Bunch의 정의에서 n실재 인입니다. 그것은 무엇이든 될 수 있으며 실제로 그것이 무엇인지는 유형에 영향을 미치지 않습니다. 모든 낱단은 같은 유형입니다. 이것은 bunches로 할 수있는 일을 어느 정도 제한합니다. 또는 묶음에 기능 목록의 길이로 태그를 지정할 수 있습니다. 그것은 모두 당신이 무엇을 필요로하는지 결국이 물건.

관련 문제