레코드의 단일 필드를 할당하고 나머지 필드를 복사하는 간단한 방법은 무엇입니까?


119

다음 레코드 ADT가 있다고 가정 해 보겠습니다.

data Foo = Bar { a :: Integer, b :: String, c :: String }

필드 중 하나를 제외한 모든 필드가 인수로 전달 된 값과 동일한 값을 갖는 레코드 (같은 유형의)를 반환하는 함수를 원합니다.

walkDuck x = Bar { a = a x, b = b x, c = lemonadeStand (a x) (b x) }

위의 내용은 작동하지만 필드가 더 많은 레코드 (예 10:)의 경우 이러한 기능을 만들려면 많은 입력이 필요하므로 불필요하다고 생각합니다.

덜 지루한 방법이 있습니까?


3
업데이트를위한 레코드 구문이 존재하지만 금방 번거로워집니다. 대신 렌즈 를 살펴보십시오 .
Cat Plus Plus

답변:


155

예, 레코드 필드를 업데이트하는 좋은 방법이 있습니다. GHCi에서는 할 수 있습니다.

> data Foo = Foo { a :: Int, b :: Int, c :: String }  -- define a Foo
> let foo = Foo { a = 1, b = 2, c = "Hello" }         -- create a Foo
> let updateFoo x = x { c = "Goodbye" }               -- function to update Foos
> updateFoo foo                                       -- update the Foo
Foo {a = 1, b = 2, c = "Goodbye" }

9
RecordWildCards확장 범위에서 "압축 풀기"필드뿐만 아니라 좋은 될 수 있습니다. 업데이트의 경우 그건 꽤 좋은 생각과 같은 :incrementA x@Foo{..} = x { a = succ a }
존 퍼디

2
BTW, Frege (JVM을위한 Haskell)에서는 함수를 다음과 같이 정의합니다 updateFoo x = x.{ c = "Goodbye" }( .연산자 참고 ).
0dB


감사. 슬프게도 내가 Haskell을 쓴 지 오래되었습니다!
크리스 테일러 (Chris Taylor)

37

이것은 렌즈에 좋은 일입니다 .

data Foo = Foo { a :: Int, b :: Int , c :: String }

test = Foo 1 2 "Hello"

그때:

setL c "Goodbye" test

'test'의 필드 'c'를 문자열로 업데이트합니다.


5
렌즈와 같은 패키지는 종종 필드를 가져오고 설정하는 기능 외에도 연산자를 정의합니다. 예를 들어, test $ c .~ "Goodbye"어떻게 lensiirc. 나는 이것이 직관적이라고 말하는 것은 아니지만, 연산자를 알게되면 $.
Thomas M. DuBuisson 2013

3
어디 있는지 아세요 SETL가 왔다? Control.Lens를 가져오고 있지만 ghc는 setL 이 정의되지 않았다고 보고합니다 .
dbanas

1
대신 SETL 세트 사용
Subhod I

16

보조 기능을 정의하거나 렌즈를 사용할 필요가 없습니다. Standard Haskell에는 이미 필요한 것이 있습니다. Don Stewart의 예를 들어 보겠습니다.

data Foo = Foo { a :: Int, b :: Int , c :: String }

test = Foo 1 2 "Hello"

그런 다음 test { c = "Goodbye" }업데이트 된 레코드를 받으라고 말하면 됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.