내 유형 정의가 주어지면 :
data Tile = Revealed | Covered deriving (Eq, Show)
data MinePit = Clean | Unsafe deriving (Eq, Show)
data Flag = Flagged | Unflagged deriving (Eq, Show)
type Square = (Tile, MinePit, Flag)
type Board = [[Square]]
두 가지 기능을 만들었습니다.
createBoard :: Int -> Int -> Board
createBoard 0 _ = [[]]
createBoard _ 0 = [[]]
createBoard 1 1 = [[(Covered, Clean, Unflagged)]]
createBoard n m = take n (repeat (take m (repeat (Covered, Clean, Unflagged))))
예 :
λ> createBoard 2 3
[[(Covered,Clean,Unflagged),(Covered,Clean,Unflagged),(Covered,Clean,Unflagged)],[(Covered,Clean,Unflagged),(Covered,Clean,Unflagged),(Covered,Clean,Unflagged)]]
defineIndices :: Int -> Int -> [[(Int,Int)]]
defineIndices n m = [[(i,j) | j <- [1..m]] | i <- [1..n]]
다음과 같이 작동합니다.
λ> defineIndices 2 3
[[(1,1),(1,2),(1,3)],[(2,1),(2,2),(2,3)]]
여기에서 특정 Square의 값을 인덱스를 통해 조회 할 수있는 MapBoard 를 만드는 함수를 만들었 습니다.
type MapBoard = Map (Int, Int) Square
createMapBoard :: [[(Int,Int)]] -> [[Square]] -> MapBoard
createMapBoard indices squares = M.fromList $ zip (concat indices) (concat squares)
그러나 Int (s) 쌍에서 직접 MapBoard를 생성하여 이전 기능을 구현할 수있는 메서드도 작성해야한다는 것이 합리적이었습니다. 다음과 같이 보일 수 있습니다.
createMapBoard2 :: Int -> Int -> MapBoard
createMapBoard2 n m = createMapBoard indices squares where
indices = defineIndices n m
squares = createBoard n m
그러나 createMapBoard를 사용하여이 상황에서 다형성을 달성 할 수 있는지 여부를 조사하고 createMapBoard2를 대신 createMapBoard로 설정했습니다. 나는 이것이 Ad-Hoc Polymorphism이라고 불리는 것을 온라인 에서 발견했습니다.
class Square a where
square :: a -> a
instance Square Int where
square x = x * x
instance Square Float where
square x = x * x
비슷한 것을 직접 작성하려고 시도했을 때 제가 생각 해낼 수있는 최선의 방법은 다음과 같습니다.
class MyClass a b MapBoard where
createMapBoard :: a -> b -> MapBoard
instance createMapBoard [[(Int,Int)]] -> [[Square]] -> MapBoard where
createMapBoard indices squares = M.fromList $ zip (concat indices) (concat squares)
instance createMapBoard Int -> Int -> MapBoard where
createMapBoard n m = createMapBoard indices squares where
indices = defineIndices n m
squares = createBoard n m
이를 컴파일하려고하면 컴파일 오류가 발생합니다.
src/minesweeper.hs:35:19-26: error: …
Unexpected type ‘MapBoard’
In the class declaration for ‘MyClass’
A class declaration should have form
class MyClass a b c where ...
|
Compilation failed.
λ>
클래스 정의에서 MapBoard와 같은 비 대수 유형을 사용할 수없는 이유가 혼란 스럽습니다.
class MyClass a b MapBoard where
MapBoard를 다른 대수 유형 c로 대체하면 또 다른 컴파일 오류가 발생하여 손실됩니다.
src/minesweeper.hs:37:10-63: error: …
Illegal class instance: ‘createMapBoard [[(Int, Int)]]
-> [[Square]] -> MapBoard’
Class instances must be of the form
context => C ty_1 ... ty_n
where ‘C’ is a class
|
src/minesweeper.hs:39:10-46: error: …
Illegal class instance: ‘createMapBoard Int -> Int -> MapBoard’
Class instances must be of the form
context => C ty_1 ... ty_n
where ‘C’ is a class
|
Compilation failed.
createMapBoard의 임시 다형성을 얻을 수 있습니까? 반환 유형이 모든 인스턴스에 대해 MapBoard 여야한다는 엄격한 제약이있는 클래스 정의를 만들 수 있습니까?
편집하다:
구문 오류를 수정 한 후 내 코드는 다음과 같습니다.
class MyClass a b where
createMapBoard :: a -> b
instance createMapBoard [[(Int,Int)]] [[Square]] where
createMapBoard indices squares = M.fromList $ zip (concat indices) (concat squares)
instance createMapBoard Int Int where
createMapBoard n m = createMapBoard indices squares where
indices = defineIndices n m
squares = createBoard n m
이로 인해 또 다른 컴파일 오류가 발생합니다.
src/minesweeper.hs:37:10-23: error: …
Not in scope: type variable ‘createMapBoard’
|
src/minesweeper.hs:39:10-23: error: …
Not in scope: type variable ‘createMapBoard’
|
Compilation failed.
나는 수업에 대한 나의 이해에 여전히 오류가 있다고 믿는 경향이 있습니다.
다음과 같이 작성하고 싶습니다.
class MyClass a b where createMapBoard :: a -> b -> MapBoard
instance MyClass [[(Int,Int)]] [[Square]] where
createMapBoard indices squares = M.fromList $ zip ...
instance MyClass Int Int where
createMapBoard n m = createMapBoard indices squares where
...
는 ... -> ... -> MapBoard
이미 createMapBoard
이 클래스 / 인스턴스 머리에 속하지 않는, 방법의 서명.
덧붙여서, 나는 여기에서 수업을 갖는 것이 정말로 의미가 있다고 확신하지 않습니다. 두 개의 개별적으로 명명 된 createMapBoard
함수 를 갖는 것은 잘못된 것이 아닙니다 . 클래스는 실제로 다형성 함수를 작성할 수있을 때만 갈 수있는 길이지만,이 경우에는 의심 스럽습니다. 한 버전이 필요한 구체적인 상황이나 다른 버전이 필요합니다. 수업이 필요하지 않고 원하는 버전을 하드 쓰기 만하면됩니다.
클래스 메서드가 아닌 별도의 함수를 사용하는 한 가지 이유는 유형 검사기의 작업을 더 쉽게 만들어주기 때문입니다. 의 인수 createMapBoard
가 다형성이 되 자마자 잠재적으로 모든 유형을 가질 수 있습니다 (적어도 유형 검사기에 관한 한). 따라서 다른 곳에서 유형이 완전히 결정된 인수로만 호출 할 수 있습니다. 이제 다른 프로그래밍 언어에서 전달하려는 값의 유형은 일반적으로 어쨌든 고정되어 있지만 Haskell에서는 실제로 다형성 값 도 갖는 것이 매우 일반적 입니다. 가장 간단한 예는 숫자 리터럴 입니다. 유형은 Int
없지만 Num a => a
.
저는 개인적으로 "역 다형성"이 일반적으로 "순방향 다형성"보다 작업하는 것이 더 좋다고 생각 합니다. 함수에 대한 인수 를 다형성으로 만들지 말고 결과를 만듭니다. 이렇게하면 가장 바깥 쪽 유형의 표현식이 환경에 의해 고정되고 자동으로 모든 하위 표현식이 유형 검사기에 의해 유추됩니다. 다른 방법으로, 모든 개별 표현식의 유형을 수정해야하며 컴파일러는 최종 결과 유형을 추론 할 수 있습니다. 어쨌든 서명으로 수정하고 싶기 때문에 거의 유용하지 않습니다.
이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.
침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제
몇 마디 만하겠습니다