Koras02
Koras02코딩웹
Koras02
전체 방문자
오늘
어제
  • 분류 전체보기 (299)
    • 백엔드 (59)
      • nestjs (2)
      • Ruby (3)
      • PostgresQL (11)
      • MySQL (5)
      • Haskell (7)
      • Koa (3)
      • Java (3)
      • Python (5)
      • Rust (5)
      • MongoDB (2)
      • PHP (3)
      • Spring Boot (1)
      • redis (5)
      • deno (2)
    • 웹서버 (3)
      • nginx (1)
      • Apache (2)
      • Google Web Server (0)
    • 모바일개발 (5)
      • Figma (0)
      • React Native (2)
      • swift (0)
      • Flutter (3)
      • Kotlin (0)
    • 프론트 엔드 (158)
      • HTML (34)
      • CSS (7)
      • Javascript (35)
      • Angular (0)
      • Typescript (2)
      • React (58)
      • Vue (2)
      • GIT (6)
      • GraphQL (1)
      • Doker (4)
      • Go (8)
      • svelte (1)
      • gatsby (0)
    • etc. (47)
      • Notion (0)
      • TIL (24)
      • Algorithm (17)
      • Algorithm 개념 정리 (2)
      • Wiki (3)
      • Official document (1)
    • 웹개념 (12)
    • 변수정리 (1)
    • VSCode (2)
    • 포트폴리오 분석 (2)
      • React (2)
    • os (5)
      • 윈도우 (4)
      • Mac (0)
      • 가상머신 (0)
      • linux (1)
    • 응용프로그램언어 (2)
      • C (2)
      • C++ (0)
      • C# (0)
    • 블로그 운영관련 (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록
글쓰기

공지사항

  • [공지사항] 개발 이슈나 공식문서 업데이트 업로드 예정입니⋯

인기 글

태그

  • CSS
  • 프로그래머스
  • 문자열
  • PostgreSQL
  • go
  • Rust
  • 알고리즘
  • redis
  • 데이터 타입
  • 하스켈
  • Java
  • html
  • javascript
  • Haskell
  • mysql
  • Til
  • html5
  • React
  • koa
  • Flutter

티스토리

최근 댓글

최근 글

250x250
hELLO · Designed By 정상우.
Koras02

Koras02코딩웹

[Haskell] 하스켈 기초반 5강 - 타입의 기초2
백엔드/Haskell

[Haskell] 하스켈 기초반 5강 - 타입의 기초2

2023. 2. 5. 22:55
728x90

이번 5강에서는 지난번 배운 타입의 기초에

2번째 시간입니다. 

 

이번 장에서는 숫자 타입들이 하스켈에서

어떻게 처리되는지 보여주고 

 

타입 시스템의 몇 가지 중요한 특성을 

소개하는 시간입니다.

 

Num 클래스

 

수학에는 함께 더할 수 있는 수의

종류에 몇가지 제약이 존재합니다. 

 

예를 들어서 2 + 3(두 자연수)

(-7) + 5.12(음의 정수와 실수) 

 

1/7 + π(유리수와 무리수)..

등이 있습니다.이것들은 모두 타당하며

 

사실 모든 임의의 두 실수는

덧셈이 가능합니다. 

 

그런 일반성을 가장 단순하게 

포착하려면 하스켈에서는 일반화된

Number 타입이 필요하고 

 

그런 (+)의 시그니처는 단순히 

아래와 같아야 합니다.

(+) :: Number -> Number -> Number

하지만 이런 설계는 컴퓨터가 산수를 

하는 방식에 잘 들이맞지 않고

 

컴퓨터는 정수를 메모리 내 일련의

이진수로 다룰 수 있으나 

 

실수에는 이 접근법이 먹히지 않습니다.

그러므로 실수를 다루려면 부동소수점 

이라는 더 복잡한 인코딩이 필요합니다.

 

부동소수점은 일반적으로 실수를 다루는 

합리적인 수단이지만 불편함 점도 있어 

 

정수에는 더 간단한 인코딩을 사용할 

가치가 있으며 즉 우리는 숫자를 

 

저장하는 방법을 최소한 두가지 가집니다.

하나는 정수, 하나는 일반적인 실수를

 

위한 것으로 각 접근법은 서로 다른 

하스켈 타입에 대응합니다.

 

더욱이 컴퓨터는 (+) 같은 연산을 

동일 포멧의 숫자들에 대해서만 

수행할 수 있습니다.

 

범우주적 Number 타입은 고사하고 

정수와 실수를 썩는 (+) 도 쓸수 없는 

것처럼 보입니다. 

 

그런데 하스켈은 적어도 정수들 또는 

실수들 사이에는 동일한 (+) 함수를 

사용할 수 있습니다.

 

Prelude > 3 + 4
7
Prelude > 6.56 + 5.24
11.8

리스트와 튜플을 논할 때 

함수가 다형성이라면 서로 다른

 

타입의 인자들을 받아들일 수 있는

걸 보았습니다. 어떤 사실을 

 

고려해보면 ( + )의 타입 시그니처로

이런게 가능할 것 같습니다.

 

(+) :: -> a -> a -> a

(+)는 a라는 동일 타입의 두 인수를 취해

a 타입의 결과로 평가할 것 입니다.

 

하지만 이 해결책에는 문제가 있는데

앞서 봤듯 타입 변수 a는 모든 타입을 

나타낼 수 있습니다.

 

(+)의 타입 시그니처가 이것이라면 

두 Bool도 두 Char로 더할 수 있다는 

건데 그다지 말이 되지 않습니다.

 

대신 (+)의 실제 시그니쳐는 

한 언어 특성을 활용하는데 이 특성은

 

a가 숫자 타입인 한 어느 타입이든 

될 수 있다고 의미상 제한을 거는 것을 

가능케 합니다.

 

(+) :: (Num a) => a -> a -> a

Num은 타입클래스로, 숫자로 간주되는

모든 타입을 아우르는 타입들의 모임입니다.

 

시그니쳐의 (Num a) => 부분은 a를 

숫자형으로, 좀 더 정확하게는 

Num의 인스턴스들으로 제한합니다.

 

숫자 타입들 

 

그러면 시그니쳐에서 a가 나타내는 

Num의 인스턴스라는 건 실제로는 

 

무슨 숫자 타입일까요? 가장 중요한 

숫자 타입은 Int, Integer, Double입니다.

 

  • Int 는 대부분 언어의 그 정수 타입에 대응합니다.
    컴퓨터의 프로세서에 따라 고정된 최댓갑과 
    최소값을 가집니다.
    (32 비트 기계에서는 -2147483648에서 2147483647까지)
  • Integer도 정수를 위해 쓰이지만 Int와 달리 효율성을
    조금 희생해서 임의 크기의 값을 지원합니다.
  • Double은 배정밀도 부동소수점 타입으로, 
    대다수의 경우 실수를 위한 좋은 선택입니다.
    (Float 이라고 Double의 단정밀도 친구가 있는데 정밀도의 
    추가 손실 때문에 대개는 Double에 밀립니다.)

이들 타입은 하스켈에서 기본적으로 

사용할 수 있고 일괄 작업에서

쓰게 될 것들 입니다. 

 

다형성 추측

아직 설명하지 않은게 하나 더 있는데 

시작부에서 언급한 덧셈 예시를 

 

시도해봤다면 이런 것이 완벽히 

타당하단 걸 알고 있을 것 입니다. 

Prelude> (-7) + 5.12
-1.88

여기서는 서로 다른 타입의 두 숫자를 

더하는 것 같습니다. 정수와 정수가 아닌 것...

(+)의 타입 때문에 불가능 한 것인가?

 

이 물음에 답하려면 우리가 입력한 

숫자들이 실제로 무슨 타입인지 봐야 합니다.

Prelude> :t (-7)
(-7) :: Num a => a

(-7)은 Int도 Integer도 아니며 이것은

다형성 상수로서 필요한 어느 숫자 타입으로든 

 

변할 수 있으며 그 이유로는 

다른 숫자를 살펴보면 명확해 집니다.

Prelude> :t 5.13
5.13 :: Fractional p => p

5.13도 다형성 상수인데 Num보다 제한적인

Functional 클래스에 속하는 것 입니다.

 

모든 Functional은 Num이지만 모든 Num이

Functional인 것은 아닙니다. 

 

하스켈 프로그램은 (-7) + 5.12를 평가할 때,

이 숫자들의 실제 타입을 결정해야 합니다.

 

그 방법은 클래스 명세를 고려하여 

타입 추론을 수행하는 것입니다.

 

(-7)은 어떤 Num도 될 수 있지만 5.13에는

추가적인 제한이 있어서 5.13의 타입인

 

(-7)이 무엇이 될 지를 결정할 것입니다.

둘의 타입이 어때야 하는지에 대한

 

다른 단서가 없기 때문에 5.12는 

기본적인 Functional 타입인 Double로

간주될 것 입니다. 

 

따라서 (-7)도 Double이 되고 덧셈을 

진행하는 것이 가능하여 Double을 

반환받게 됩니다.

 

이  과정을 더 와닿게 하는 빠른 검사가 있습니다.

소스 파일에서는 이렇게 정의합니다.

x = 2

그리고 이 파일을 GHCi로 불러와 x의

타입을 확인합니다. 그 다음 파일에 

변수 y를 추가합니다.

x = 2 
y = x + 3

파일을 다시 불러와 x와 y의 타입을 

확인합니다. 마지막으로 y를 수정 후

x = 2
y = x + 3.1

두 변수의 타입이 어떤 값을 불러오는지 

확인해 볼 수 있습니다.

 

단일형 문제

 

숫자 타입과 클래스의 정교함은 

복합적으로 이어지기도 합니다.

 

가령, 흔한 나누기 연산자인 ( / )를 

보면 그 타입 시그니처는 다음과 같습니다.

(/) :: (Fractional a) => a -> a -> a

a를 분수형으로 제한하는 것은

필수사항인데 두 정수의 나눗셈 결과는

 

대개 정수가 아니기 때문입니다. 

그래도 여전히 이렇게 작성할 수 있습니다.

Prelude> 4 / 3
1.3333333333333333

리터럴 4와 3은 다형성 상수고 ( / )의 

요구에 따라 Double 타입으로

간주되기 때문입니다.

 

하지만 한 숫자를 리스트의 길이로

나누는 것을 가정해 봅시다.

 

당연히 length 함수를 사용해야 

할 것입니다. 

Prelude> 4 / length [1,2,3]

하지만 이런 오류가 발생하는데

<interactive>:1:0:
    No instance for (Fractional Int)
      arising from a use of `/' at <interactive>:1:0-17
    Possible fix: add an instance declaration for (Fractional Int)
    In the expression: 4 / length [1, 2, 3]
    In the definition of `it': it = 4 / length [1, 2, 3]

평소와 마찬가지로 이 문제는 length의

타입 시그니쳐를 보면 이해할 수 있습니다.

length :: [a] -> Int

length의 결과는 다형성 상수가 아니라

Int입니다. 그리고 Int는 Functional이 

 

아니기 때문에 ( / )의 시그니쳐에

들어맞지 않습니다.

 

이 문제에서 탈출하는 수단을 제공하는

편리한 함수가 있습니다.

fromIntegral :: (Integral a, Num b) => a -> b

fromIntegral은 Integral 타입의 무언가를

인자로 취해서 다형성 상수로 만듭니다.

 

length와 결합하면 리스트의 길이를 

( / )의 시그니쳐에 맞출 수 있습니다.

Prelude> 4 / fromIntegral (length [1,2,3])
1.3333333333333333

처음에는 이 표현식이 과도하게 복잡해 

보이겠지만 이런 방법이 숫자를 조작할 때 

표현식을 더 철처하게 만듭니다. 

 

인자가 Int인 함수를 정의하면 그 인자는 

절대 알아서 Integer나 Duble로

변환되지 않습니다.

 

fromIntegral 같은 함수로 프로그램에게 

그 일을 명확히 지시해야 합니다.

 

이런 절제된 타입 체계의 결과로 

하스켈에서는 숫자를 다루는 놀랍도록

다양한 클래스와 함수들이 있습니다.

 

숫자 너머의 클래스

 

타입클래스의 용도는 산술을 넘어 

많은 것이 있습니다.

 

가령 (==)의 타입 시그니쳐는 

다음과 같습니다.

(==) :: (Eq a) => a -> a -> Bool

(+) 나 ( / )처럼 (==)도 다형성 함수입니다.

(==)는 같은 타입의 두 값을 비교해 

 

Bool 값을 반환하는데, 이 두 값은 

반드시 Eq 클래스에 속해야 합니다.

 

Eq는 항등 비교가 가능한 값들의

타입들의 클래스이며 모든 기본적인

비함수형 타입들이 여기에 포함됩니다. 

 

타입클래스는 타입 체계에 많은 강력함을

보태는 아주 범용적인 언어 특성입니다.

 

참고 자료

 

6 타입의 기초2

원문: [http://en.wikibooks.org/wiki/Haskell/Type_basics_II](http://en.wikibooks.org/wiki/Haskell/Type…

wikidocs.net

 

'백엔드 > Haskell' 카테고리의 다른 글

[Haskell] 하스켈 기초반 6강 - 패턴 매칭과 if 표현식 및 let 바인딩  (0) 2023.02.17
[Haskell] 하스켈 기초반 4강 - 리스트와 튜플  (0) 2023.01.29
[Haskell] 하스켈 기초반 3강 - 타입의 기초  (0) 2023.01.12
    '백엔드/Haskell' 카테고리의 다른 글
    • [Haskell] 하스켈 기초반 6강 - 패턴 매칭과 if 표현식 및 let 바인딩
    • [Haskell] 하스켈 기초반 4강 - 리스트와 튜플
    • [Haskell] 하스켈 기초반 3강 - 타입의 기초
    • [Haskell] 하스켈 기초반 2강 - 진위값
    Koras02
    Koras02
    현재 사용중인 언어 - next-js,react,vue, typescript

    티스토리툴바