Можно ли определить чисто воображаемый тип в Haskell?

Я собираюсь использовать C++ в качестве примера, чтобы показать, что мне нужно. Для сложной арифметики он имеет как комплексный, так и мнимый типы:

https://en.cppreference.com/w/c/language/arithmetic_types#Imaginary_floating_types

Эти, например. обладают тем свойством, что умножение двух чисел с мнимым типом double будет иметь тип double. Это почти то же самое, но не совсем то же самое, что и использование комплексных чисел с реальной частью, равной 0,0, но не совсем. Воображаемые типы не сохраняют явным образом, автоматически устраняют ненужные вычисления и сохраняют 0.0.

Кроме того, это предотвращает некоторые проблемы с нулями со знаком. Например. вычисление (0,0+i*a)*(0,0+i*b) приводит к (-a*b-i*0,0), если a и b отрицательны, и (-a*b+i*0,0) в противном случае. Это может удивить, если результат передается в функцию с отсечением ветвления. Воображаемый тип избегает этого нежелательного отрицания нуля.

Мой вопрос: можете ли вы определить аналогичный воображаемый тип в Haskell (в дополнение к сложному типу), а также операции (+), (-), (*) и (/) для него, чтобы они вели себя как в С++? Кажется, что, по крайней мере, с текущим определением классов Num и Fractional это невозможно, потому что (+), (-), (*) и (/) имеют сигнатуру типа a -> a -> a as, например в результате умножение двух мнимых чисел не может иметь другой тип. Однако можно ли дать другое определение для этих классов, чтобы было возможно то, что мне нужно?

Я не прошу об этом с практической целью. Я просто хочу лучше понять, на что способна система типов Haskell.

Отвечает ли это на ваш вопрос? Как создать общий сложный тип в haskell?

monk 08.05.2022 01:41

@monk Нет. Этот вопрос касается сложного типа как с реальной, так и с мнимой частью, тогда как я спрашиваю о типе, который будет иметь только мнимую часть.

QuantumWiz 08.05.2022 11:37
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
129
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Да, конечно, вы можете определить такой тип. Вы просто не сможете использовать интерфейс Num для всех его операций; но вы можете определить любые другие функции, которые вы хотите, с другими типами, возможно, даже сделав их инфиксными операторами, если это необходимо.

Вот пример типа, который отслеживает на уровне типа, является ли он воображаемым или реальным, и поддерживает сложение и умножение с альтернативным именем (вычитание и деление не требуют каких-либо новых идей, не показанных здесь):

{-# Language DataKinds #-}
{-# Language KindSignatures #-}
{-# Language TypeFamilies #-}

-- hide the Mindful data constructor
module Mindful (Mindful, real, iTimes, (+.), (*.), EqBool, KnownReality(isReal)) where

newtype Mindful (reality :: Bool) a = Mindful a deriving (Eq, Ord, Read, Show)

real :: a -> Mindful True a
real = Mindful

iTimes :: a -> Mindful False a
iTimes = Mindful

(+.) :: Num a => Mindful r a -> Mindful r a -> Mindful r a
Mindful x +. Mindful y = Mindful (x + y)

(*.) :: (KnownReality r, KnownReality r', Num a)
     => Mindful r a -> Mindful r' a -> Mindful (EqBool r r') a
xm@(Mindful x) *. ym@(Mindful y) = Mindful (iSquared * x * y) where
    iSquared = if isReal xm || isReal ym then 1 else -1

type family EqBool a b where
    EqBool False False = True
    EqBool False True = False
    EqBool True False = False
    EqBool True True = True

class KnownReality r where isReal :: Mindful r a -> Bool
instance KnownReality False where isReal _ = False
instance KnownReality True where isReal _ = True

Если вам по какой-то причине нужно, чтобы имена были именно + и т. д. (я не рекомендую этого, это будет большая боль), вы можете взглянуть на еще один мой ответ об управлении пространством имен.

Трудность, с которой я столкнулся, заключается в определении (+), (-), (*) и (/) для этого типа, что, вероятно, требует переопределения Num и Fractional, если это возможно. Я немного отредактировал свой вопрос, чтобы, надеюсь, сделать его более понятным. Было бы немного неудобно использовать разные не перегружаемые функции для каждой комбинации типов.

QuantumWiz 08.05.2022 11:42

@QuantumWiz Вы не должны имеют использовать Num для предоставления определений для +, - и т. д. только потому, что они находятся в стандартной прелюдии гордости. Num — это обычный класс с обычными методами и экземплярами. Вы можете предоставить свои собственные определения этих символов (даже свой собственный класс для определения этих символов со многими различными типами), если желаемое использование не соответствует ограничениям существующего класса. Это полностью зависит от вас, стоит ли выгода от вызова вашей операции + вместо выбора другого символа хлопот сокрытия определения Prelude.

Ben 08.05.2022 14:10

@Ben Предоставленных определений Num (и Fractional) явно недостаточно. Проблема, с которой я сталкиваюсь, заключается в том, как сделать свои собственные определения. Я совершенно уверен, что мне придется использовать свои собственные классы, поскольку кажется, что я не могу создавать перегруженные функции без классов. Однако я не могу понять, как написать свои собственные классы (вместо Num и Fractional), чтобы иметь воображаемые и сложные типы с желаемым поведением.

QuantumWiz 08.05.2022 15:34

@QuantumWiz Дело в том, что вы не знаете, как писать классы с нужным поведением, или в том, что вы не знаете, как использовать свои собственные классы вместо Num и Fractional?

Ben 08.05.2022 16:23

@QuantumWiz Я добавил текст, описывающий один из способов создания такого типа без использования Num, и ссылку на текст о том, что делать, если вам действительно нравятся имена, которые предлагает Num. Я считаю, что это касается вашего обновленного вопроса.

Daniel Wagner 08.05.2022 19:07

@Ben Первый, т. Е. Я не знаю, как писать классы с нужным мне поведением. Теперь я прочитаю и попытаюсь понять обновленный ответ Даниэля Вагнера. Похоже, это может быть то, что я искал.

QuantumWiz 08.05.2022 22:42

@DanielWagner Спасибо. Я думаю, что это, вероятно, в значительной степени близко к тому, что я хотел, что может быть достигнуто. Есть пара вопросов: 1) С таким определением (+.) кажется, что невозможно сложить действительные и мнимые числа. Впрочем, я думаю, что мог бы исправить это сам. Нужно просто заменить Bool на какой-то тип с 3 конструкторами для обозначения действительных, мнимых и комплексных чисел.

QuantumWiz 10.05.2022 01:14

@DanielWagner 2) Кажется, что с этим определением невозможно напрямую добавить или умножить эти типы с помощью простых чисел с плавающей запятой или удвоения без предварительного помещения их в этот тип, но я думаю, что это неизбежно. Я бы предпочел, чтобы Num можно было переписать, чтобы вы могли использовать их напрямую, но я думаю, что система типов Haskell слишком жесткая (хотя во многих отношениях она очень элегантна). Я принимаю ваш ответ, потому что думаю, что лучший ответ, вероятно, невозможен.

QuantumWiz 10.05.2022 01:27

@QuantumWiz Правильно, этот метод не позволяет складывать чисто действительные и чисто мнимые числа. Но остерегайтесь обобщать этот момент; это определение было выбрано тщательно для его представления в памяти, и добавление конструкторов резко увеличит размер представления в памяти. Я считаю, что это не система типов Haskell слишком жесткая, а именно Num слишком жесткая. Возможны альтернативные классы типов, поддерживающие смешанные аргументы (и они были испытаны до смерти; взгляните на Hackage).

Daniel Wagner 10.05.2022 04:29

@DanielWagner «добавление конструкторов увеличит размер представления в памяти», не так ли? Я думал, что это решение сделало вывод о том, что число является мнимым или реальным на уровне типа и, следовательно, предположительно во время компиляции. Если бы в этом не было необходимости, я мог бы легко реализовать нужный тип следующим образом: data MyNumber = Real Double | Imaginary Double | Complex Double Double и затем объявить его экземпляром Num. Это решит проблему нуля со знаком для действительной части чисто мнимого числа, но все равно будет выполнять действия во время выполнения, которые можно было бы сделать во время компиляции.

QuantumWiz 10.05.2022 15:16

@DanielWagner «Я считаю, что не система типов Haskell слишком жесткая, а Num, в частности, слишком жесткая». Если вы правы, это хорошая новость, потому что (гипотетическая) переписывание Num кажется мне идеальным решением. Однако, по крайней мере, я не могу понять, как переписать Num, чтобы он делал то, что я хочу здесь.

QuantumWiz 10.05.2022 15:19

@QuantumWiz Правильно, это решение делает вывод на уровне типа и, следовательно, во время компиляции. Но вы предлагаете изменить это решение, и предлагаемое вами изменение будет стоить вам денег. Один из способов переписать Num похож на class Add a b where type Sum a b; (+) :: a -> b -> Sum a b и аналогично для умножения.

Daniel Wagner 10.05.2022 16:06

Другие вопросы по теме