Как заставить Haskell различать синонимы типов

Мне нравится идея синонимов типов Haskell, потому что они позволяют различать абстрактные типы данных, которые имеют общие базовые представления. К сожалению, когда я пишу программу типа

data Vector a = Vec a a

-- Some definitions here about (+) and (*) for Vector ...

type Position = Vector Float
type Velocity = Vector Float
type Time = Float

step :: Position -> Velocity -> Time -> Position
step p v dt = p + v*dt

p :: Position
p = Vec 0.0 0.0

v :: Velocity
v = Vec 1.0 1.0

p' = step v p 0.01

Это совершенно правильный код на Haskell, несмотря на то, что v и p находятся не в тех местах. Я хотел бы усилить различие между синонимами типов, чтобы они по-прежнему имели общее базовое представление, но не принимались друг за друга в приложении функции. Это возможно?


person Austin Garrett    schedule 05.03.2018    source источник
comment
Вы должны использовать newtype вместо lf type.   -  person Shersh    schedule 05.03.2018
comment
@Shersh Чтобы ответить на вопрос, используйте кнопку «Отправить ответ». Комментарии предназначены для запроса разъяснений или предложений по улучшению вопроса.   -  person amalloy    schedule 05.03.2018
comment
Но получить достижение за многие комментарии с 5+ плюсами иначе невозможно... Политика StackOverflow поощряет меня писать такие комментарии вместо ответов!   -  person Shersh    schedule 05.03.2018
comment
@Shersh Это не оправдание. Частью задачи является написание соответствующих комментариев, за которые проголосуют.   -  person chepner    schedule 05.03.2018
comment
@amalloy, иногда можно написать комментарий (например, Шерша) за несколько секунд и помочь ОП. Но за это время можно не написать правильный ответ. Вышеупомянутый комментарий, вероятно, не был бы хорошо принят, если бы он представлялся как ответ.   -  person dfeuer    schedule 06.03.2018
comment
@dfeuer Да, я знаю, что это идея, но я не думаю, что это стоит поощрять. То, на что можно легко ответить в комментарии из семи слов, также быстро получит настоящий ответ — в этом случае исчерпывающий ответ пришел всего через две минуты после ответа на комментарий. Я бы предпочел, чтобы мы получили настоящий ответ, и наличие подобного комментария не поощряет публикацию ответа, потому что ОП уже помогли.   -  person amalloy    schedule 06.03.2018
comment
Из wiki.haskell.org/Newtype: данные могут быть заменены на newtype только в том случае, если тип имеет ровно один конструктор только с одним полем внутри него. Я полагаю, что мог бы просто сделать newtype Vector a = Vector (a, a).   -  person Austin Garrett    schedule 07.03.2018


Ответы (2)


Вы можете сделать Vector фантомным типом следующим образом:

data Vector t a = Vec a a

data Pos
data Vel

type Position = Vector Pos Float
type Velocity = Vector Vel Float

Теперь вы можете определить экземпляры Position и Velocity, как обычно:

p :: Position
p = Vec 0.0 0.0

v :: Velocity
v = Vec 1.0 1.0

Однако это не позволит вам использовать их взаимозаменяемо:

type Time = Float

step :: Position -> Velocity -> Time -> Position
step p v dt = p + v*dt -- you might have to change this definition

p' = step v p 0.01 -- won't compile

Вы также можете сделать вещи более точными, используя DataKinds и KindSignatures:

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}

data VectorType = Pos | Vel

data Vector (t :: VectorType) a = Vec a a

type Position = Vector Pos Float
type Velocity = Vector Vel Float

Надеюсь, это поможет.

person Aadit M Shah    schedule 05.03.2018
comment
Я думаю, что в наши дни чаще говорят, что конструктор типа имеет один или несколько фантомных параметров, чем говорят, что это фантомный тип. - person dfeuer; 06.03.2018
comment
Кажется, я видел похожее решение для C++. К сожалению, не так элегантно, как я надеялся (тем более, что его нельзя применить к типам данных, которые определены в импортированных библиотеках), но, тем не менее, полезно. - person Austin Garrett; 07.03.2018

newtype скорее всего то, что вам нужно, или, во всяком случае, лучшее, что у нас есть. Как и type, он определяет новое имя для существующего типа, и представление во время выполнения будет таким же. В отличие от type (но как и data), они считаются разными при проверке типов, и есть новый конструктор данных.

Итак, у вас может быть такой код:

newtype Position = Position (Vector Float)
p :: Position
p = Position (Vec 0 0)
person bergey    schedule 05.03.2018