문제

두 가지 데이터 유형 Foo와 Bar가 있다고 가정합니다. Foo에는 필드 X와 Y가 있습니다. 바에는 필드 X와 Z가 있습니다. FOO 또는 막대를 매개 변수로 취하고 x 값을 추출하고 계산을 수행 한 다음 그에 따라 x 값을 설정 한 새 foo 또는 막대를 반환하는 함수를 작성할 수 있기를 원합니다.

다음은 다음과 같습니다.

class HasX a where
    getX :: a -> Int
    setX :: a -> Int -> a

data Foo = Foo Int Int deriving Show

instance HasX Foo where
    getX (Foo x _) = x
    setX (Foo _ y) val = Foo val y

getY (Foo _ z) = z
setY (Foo x _) val = Foo x val

data Bar = Bar Int Int deriving Show

instance HasX Bar where
    getX (Bar x _) = x
    setX (Bar _ z) val = Bar val z

getZ (Bar _ z) = z
setZ (Bar x _) val = Bar x val

modifyX :: (HasX a) => a -> a
modifyX hasX = setX hasX $ getX hasX + 5

문제는 모든 getters와 setter가 글을 쓰는 데 고통스러워한다는 것입니다. 특히 Foo와 Bar를 많은 필드가있는 실제 데이터 유형으로 바꾸는 경우.

Haskell의 레코드 구문은 이러한 레코드를 정의하는 훨씬 더 좋은 방법을 제공합니다. 하지만 이와 같은 레코드를 정의하려고하면

data Foo = Foo {x :: Int, y :: Int} deriving Show
data Bar = Foo {x :: Int, z :: Int} deriving Show

X가 여러 번 정의된다는 오류가 발생합니다. 그리고 나는 유형 클래스 의이 부분을 만들어 modifyx로 전달할 수있는 방법을 보지 못했습니다.

이 문제를 해결하는 멋진 깨끗한 방법이 있습니까, 아니면 내 자신의 게터와 세터를 정의하는 데 고집되어 있습니까? 다시 말해서, 레코드 구문으로 생성 된 기능을 유형 클래스 (getters and setters)와 연결하는 방법이 있습니까?

편집하다

내가 해결하려는 진짜 문제는 다음과 같습니다. 나는 모든 System.console.getOpt를 사용하는 일련의 관련 프로그램을 작성하여 명령 줄 옵션을 구문 분석합니다. 이러한 프로그램에서 공통적 인 많은 명령 줄 옵션이 있지만 일부 프로그램에는 추가 옵션이있을 수 있습니다. 각 프로그램이 모든 옵션 값이 포함 된 레코드를 정의 할 수 있기를 바랍니다. 그런 다음 기본 레코드 값으로 시작하여 Statet Monad를 통해 변환되어 getOpt를 통해 명령 줄 인수를 반영하는 최종 레코드를 얻습니다. 단일 프로그램의 경우이 접근 방식은 실제로 잘 작동하지만 모든 프로그램에서 코드를 재사용하는 방법을 찾으려고 노력하고 있습니다.

도움이 되었습니까?

해결책

당신은 원합니다 확장 가능한 기록 내가 모은 것은 Haskell에서 주제에 대해 가장 많이 이야기하는 것 중 하나입니다. 현재이를 구현하는 방법에 대한 합의가 많지 않은 것으로 보입니다.

귀하의 경우에는 일반 레코드 대신에 구현 된 것과 같은 이기종 목록을 사용할 수 있습니다. hlist.

다시 말하지만, 여기에는 공통점과 프로그램의 두 가지 수준 만있는 것 같습니다. 따라서 공통 옵션의 공통 레코드 유형과 각 프로그램의 프로그램 별 레코드 유형을 정의하고 해당 유형의 튜플에서 Statet을 사용해야합니다. 일반적인 것들에 대해서는 구성하는 별칭을 추가 할 수 있습니다. fst 공통 액세서가 있으므로 발신자에게는 보이지 않습니다.

다른 팁

다음과 같은 코드를 사용할 수 있습니다

data Foo = Foo { fooX :: Int, fooY :: Int } deriving (Show)
data Bar = Bar { barX :: Int, barZ :: Int } deriving (Show)

instance HasX Foo where
  getX = fooX
  setX r x' = r { fooX = x' }

instance HasX Bar where
  getX = barX
  setX r x' = r { barX = x' }

코드에서 무엇을 모델링하고 있습니까? 우리가 문제에 대해 더 많이 알고 있다면, 우리는이 객체 지향 디자인보다 기능적인 언어로 삐걱 거리는 것보다 덜 어색한 것을 제안 할 수 있습니다.

제네릭의 직업처럼 보인다. int가 다른 NewTypes로 태그를 붙일 수 있다면 (Uniplate, Module PlateData와 함께) 작성할 수 있습니다.

data Foo = Foo Something Another deriving (Data,Typeable)
data Bar = Bar Another Thing deriving (Data, Typerable)

data Opts = F Foo | B Bar

newtype Something = S Int
newtype Another = A Int
newtype Thing = T Int

getAnothers opts = [ x | A x <- universeBi opts ]

이것은 Opts 내부의 어느 곳에서나 다른 사람을 모두 추출합니다.

수정도 가능합니다.

접이식의 유형 인스턴스를 만드는 경우 액세서의 기초로 사용할 수있는 탁월한 기능을 얻습니다.

접이식이 당신에 의해 접근 할 수없는 경우, 올바른 접근법은 유형 클래스로 원하는 인터페이스를 정의하고 파생 된 값을 자극하는 좋은 방법을 찾는 것입니다.

아마도 그렇게함으로써

deriving(Data)

GMAP 콤비네이터를 사용하여 액세스를 기반으로 할 수 있습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top