0

저는 하스켈 타입 클래스를 가지고 놀았으며, 누군가가 저를 도울 수 있기를 바랍니다. 내가 신속한 배경에서 출발하여 하스켈 코드에 프로토콜 지향 지식을 포팅하려고하는 "시도"를 생각해보십시오. 타입 클래스 인스턴스 목록

data Candle = Candle { 
    mts :: Integer, 
    open :: Double, 
    close :: Double 
} 

data Bar = Bar { 
    mts :: Integer, 
    min :: Double, 
    max :: Double 
} 

그럼 난 "클래스"를 만들기로 결정 기본적인 작업을 정의 할 :

class GenericData a where 
    dataName :: a -> String 
    dataIdentifier :: a -> Double 
    dataParsing :: a -> String -> Maybe a 
    dataEmptyInstance :: a 


instance GenericData Candle where 
    dataName _ = "Candle" 
    dataIdentifier = fromInteger . mts 
    dataParsing _ = candleParsing 
    dataEmptyInstance = emptyCandle 

instance GenericData Bar where 
    dataName _ = "Bar" 
    dataIdentifier = fromInteger . mts 
    dataParsing _ = barParsing 
    dataEmptyInstance = emptyBar 

처음에 나는 동일한 구조, 단지 다른 구현을했다 JSON 파서의 무리를 선언

첫 번째 코드 냄새가 필요하지 않을 때 "a"를 포함해야하지만 (dataName 또는 dataParsing) 나는 그 다음에 조치를 취했습니다. 나도 인스턴스가 구문 분석을 수행하기 위해 선택해야하는 경우 선택하고자 할 때 이제

analyzeArguments :: GenericData a => [] -> [String] -> Maybe (a, [String]) 
analyzeArguments [] _    = Nothing 
analyzeArguments _ []    = Nothing 
analyzeArguments name data 
    | name == "Candles" = Just (head possibleCandidates, data) 
    | name == "Bar" = Just (last possibleRecordCandidates, data) 
    | otherwise = Nothing 

possibleCandidates :: GenericData a => [a] 
possibleCandidates = [emptyCandle, emptyBar] 

, 난 항상 내 목표는 다른 때문에 GenericData의 인스턴스 목록을 만드는 것이 었습니다

• Couldn't match expected type ‘a’ with actual type ‘Candle’ 
    ‘a’ is a rigid type variable bound by 
    the type signature for: 
     possibleCandidates :: forall a. GenericData a => [a] 
    at src/GenericRecords.hs:42:29 

다음과 같은 오류가 함수는 올바른 dataParser을 실행하도록 선택된 함수에 따라 달라집니다. 이 유형의 검사기 인 * -> Constraint과 관련이 있지만이 충돌을 해결할 방법을 찾지 못했다는 것을 알고 있습니다. 몇 가지 GHC 언어 확장을 사용했지만 아무도 문제를 해결하지 못했습니다.

+2

여기 코드 냄새가 아마도 당신의 타입 클래스라고 생각합니다. 메소드가 모두'a'를 첫 번째 인자로 가지는 타입 클래스를 볼 때마다, 누군가가 객체 지향 클래스를 하스켈 타입 클래스로 구형화하려고 시도하고 있다는 것을 의심스러워합니다. 같은 ADT 하에서'Bar'와'Candle' 생성자를 넣으려는 것 같습니다. – Alec

+3

나는 이것이 알려진 반 패턴 (https://lukepalmer.wordpress.com/2010/01/24/haskell-antipattern-existential-typeclass/)을 향해 빠르게 내려갈 것이라고 생각한다. – chi

+2

무엇이 잘못되었는지에 대한 두 가지 의견 (해결책 제시 없음). 1.리스트는 동 질적 (모든 엘리먼트는 같은 타입을 가짐)이므로'[emptyCandle, emptyBar]'가 바로 나온다. 2.'Foo a => [a]'타입은이 값의 사용자에게'Foo'의 인스턴스가 선택되는 것을 제어합니다; 구현자는'Foo'의 인스턴스 인 * any * 타입의 값 목록을 생성 할 준비가되어 있어야합니다. 반면에이 유형의 구현자가 인스턴스 인 선호 유형을 선택한다고 생각하는 것 같습니다 'Foo'. –

답변

2

당신은 유형 서명이 :

possibleCandidates :: GenericData a => [a] 

어떤 당신이 일이 당신이 오래가 GenericData 그대로 그 목록에 아무것도 넣을 수 있음을 의미 할 수도 있습니다. 그러나 그것은 하스켈의 타입 시스템이 실제로 작동하는 방식이 아닙니다. possibleCandidates 값은 GenericData 클래스를 갖는 모든 유형의 목록이 될 수 있지만 목록의 모든 요소는 과 같아야합니다. 유형이어야합니다.

GHC 오류 메시지는 (그 자체의 특별한 방식으로) 목록의 첫 번째 요소가 Candle이므로 나머지 목록도 Candle 유형이어야하지만 두 번째 요소는 실제로는 Bar입니다.

이제는 하스켈에서 이기종 목록 (및 기타 컬렉션)을 만드는 방법이 있지만 거의 불가능합니다.

data GenericData = GenericCandle Candle | GenericBar Bar 

당신은 심지어 간접의 단계를 포기하고 바로 데이터 구조에 직접 촛불과 바의 데이터를 넣을 수 있습니다 :이 문제에 대한

한 일반적인 솔루션은 아래로 한 sum data type에 모든 것을 통합하는 것입니다.

지금 대신 FA 클래스는 단지 데이터 유형을 가지고 클래스의 기능이 정상적인 기능이 될 :

dataName :: GenericData -> String 
dataIdentifier :: GenericData -> Double 
dataParsing :: GenericData -> String -> Maybe a 
dataEmptyInstance :: String -> GenericData 

가이 일을하는 다른 더 복잡한 방법이 있지만, 합 데이터 유형이 법안을 맞는 경우, 그걸 써. 하스켈의 파서는 대용량 데이터 유형 (대개 재귀 적)을 그 결과로 갖는 것이 일반적입니다. 예를 들어 유형의 Aeson 표준 JSON 라이브러리를 살펴보십시오.

+1

나는이 블로그 게시물을 기반으로 비슷한 솔루션을 만들었습니다. http://www.haskellforall.com/2012/05/scrap-your-type-classes.html – Invoke