Haskell Language
레코드 구문
수색…
기본 구문
레코드는 필드 이름을 지정할 수있는 sum algebraic data
형식의 확장입니다.
data StandardType = StandardType String Int Bool --standard way to create a sum type
data RecordType = RecordType { -- the same sum type with record syntax
aString :: String
, aNumber :: Int
, isTrue :: Bool
}
그런 다음 필드 이름을 사용하여 레코드에서 명명 된 필드를 가져올 수 있습니다
> let r = RecordType {aString = "Foobar", aNumber= 42, isTrue = True}
> :t r
r :: RecordType
> :t aString
aString :: RecordType -> String
> aString r
"Foobar"
레코드는 패턴 일치와 일치 할 수 있습니다.
case r of
RecordType{aNumber = x, aString=str} -> ... -- x = 42, str = "Foobar"
모든 필드의 이름을 지정할 필요는 없습니다.
레코드는 필드 이름을 지정하여 만들지 만 일반 합계 유형으로 만들 수도 있습니다 (필드 수가 적고 변경할 가능성이없는 경우에 유용 할 때가 많습니다)
r = RecordType {aString = "Foobar", aNumber= 42, isTrue = True}
r' = RecordType "Foobar" 42 True
명명 된 필드없이 레코드가 생성되면 컴파일러에서 경고를 내고 결과 값은 undefined
.
> let r = RecordType {aString = "Foobar", aNumber= 42}
<interactive>:1:9: Warning:
Fields of RecordType not initialized: isTrue
> isTrue r
Error 'undefined'
값을 설정하여 레코드 필드를 업데이트 할 수 있습니다. 언급되지 않은 입력란은 변경되지 않습니다.
> let r = RecordType {aString = "Foobar", aNumber= 42, isTrue = True}
> let r' = r{aNumber=117}
-- r'{aString = "Foobar", aNumber= 117, isTrue = True}
복잡한 레코드 유형의 렌즈 를 만드는 것이 종종 유용합니다.
필드 값을 변경하면서 레코드 복사
이 유형이 있다고 가정합니다.
data Person = Person { name :: String, age:: Int } deriving (Show, Eq)
및 두 값 :
alex = Person { name = "Alex", age = 21 }
jenny = Person { name = "Jenny", age = 36 }
alex
에서 복사하여 변경할 값을 지정하여 Person
유형의 새 값을 작성할 수 있습니다.
anotherAlex = alex { age = 31 }
alex
및 anotherAlex
의 값은 다음과 anotherAlex
.
Person {name = "Alex", age = 21}
Person {name = "Alex", age = 31}
newtype을 사용한 레코드
레코드 구문은 정확히 하나의 필드가있는 정확히 한 생성자가 있다는 제한하에 newtype
과 함께 사용할 수 있습니다. 여기서 새로운 이점은 newtype을 언랩하는 함수를 자동으로 생성하는 것입니다. 이 필드의 이름은 종종 monads의 경우 run
시작하고 monoid의 경우 get
, 다른 유형의 경우 un
로 지정됩니다.
newtype State s a = State { runState :: s -> (s, a) }
newtype Product a = Product { getProduct :: a }
newtype Fancy = Fancy { unfancy :: String }
-- a fancy string that wants to avoid concatenation with ordinary strings
레코드 구문은 일반적으로 값을 형성하는 데 사용되지 않으며 필드 이름은 언 래핑에 엄격하게 사용된다는 점에 유의해야합니다
getProduct $ mconcat [Product 7, Product 9, Product 12]
-- > 756
RecordWildCards
{-# LANGUAGE RecordWildCards #-}
data Client = Client { firstName :: String
, lastName :: String
, clientID :: String
} deriving (Show)
printClientName :: Client -> IO ()
printClientName Client{..} = do
putStrLn firstName
putStrLn lastName
putStrLn clientID
Client{..}
패턴은 생성자 Client
의 모든 필드를 가져오고 패턴과 동일합니다
Client{ firstName = firstName, lastName = lastName, clientID = clientID }
또한 다음과 같은 다른 필드 matcher와 결합 될 수 있습니다.
Client { firstName = "Joe", .. }
이것은
Client{ firstName = "Joe", lastName = lastName, clientID = clientID }
필드 레이블을 사용하여 데이터 유형 정의
필드 레이블을 사용하여 데이터 유형을 정의 할 수 있습니다.
data Person = Person { age :: Int, name :: String }
이 정의는 데이터 형식의 일부에 액세스하는 데 사용할 수있는 레코드 접근 자도 정의하므로 일반 레코드 정의와 다릅니다.
이 예제에서는 age
와 name
필드에 각각 액세스 할 수 있도록 age
와 name
두 개의 레코드 접근자가 정의되어 있습니다.
age :: Person -> Int
name :: Person -> String
레코드 접근자는 컴파일러에 의해 자동 생성되는 하스켈 함수 일뿐입니다. 따라서, 그들은 일반 하스켈 함수처럼 사용됩니다.
필드의 이름을 지정하면 코드를보다 쉽게 읽을 수 있도록 필드 레이블을 여러 다른 컨텍스트에서 사용할 수 있습니다.
패턴 매칭
lowerCaseName :: Person -> String
lowerCaseName (Person { name = x }) = map toLower x
패턴 일치가 정의의 RHS에서 사용될 수있는 새로운 값 (이 경우 x
)과 일치하는 동안 관련 필드 레이블의 위치에있는 값을 바인딩 할 수 있습니다.
NamedFieldPuns
를 NamedFieldPuns
패턴 매칭
lowerCaseName :: Person -> String
lowerCaseName (Person { name }) = map toLower name
NamedFieldPuns
확장은 대신 우리가 일치시키고 자하는 필드 레이블을 지정할 수있게 해줍니다.이 이름은 정의의 RHS에서 그림자가 생기므로 name
은 레코드 접근자가 아닌 값을 참조합니다.
RecordWildcards
를 RecordWildcards
패턴 매칭
lowerCaseName :: Person -> String
lowerCaseName (Person { .. }) = map toLower name
RecordWildCards
사용하여 일치 RecordWildCards
때 모든 필드 레이블이 범위로 가져옵니다. (이 특정 예에서 name
과 age
)
이 확장은 Person
의 정의가 확실하지 않은 경우 값이 범위로 가져 오는 방법이 명확하지 않으므로 약간 논란의 여지가 있습니다.
업데이트 기록
setName :: String -> Person -> Person
setName newName person = person { name = newName }
필드 레이블을 사용하여 데이터 유형을 업데이트하는 특수 구문도 있습니다.