Что скобки() используются сами по себе?
Я читал в узнал, что вы haskell, который
Члены enum представляют собой упорядоченные типы... Типы в этом классе: (), Bool, Char...
Также он появляется в некоторых подписях:
putChar :: Char -> IO ()
Очень сложно найти информацию об этом в Google, поскольку ответы относятся к проблемам "общих круглых скобок" (использование в вызовах функций, проблемах с приоритетом и т.п.).
Следовательно, что означает выражение ()
? Это тип? Что такое переменные типа ()
? Что используется для и когда это необходимо?
Ответы
Ответ 1
Это тип и значение. ()
- это "выраженная" единица специального типа, и она имеет одно значение: ()
, также произносится единица. Он по существу такой же, как тип void
в Java или C/С++. Если вы знакомы с Python, подумайте об этом как о NoneType
, который имеет singleton None
.
Это полезно, когда вы хотите обозначить действие, которое ничего не возвращает. Он чаще всего используется в контексте Монад, например, монады IO
. Например, если у вас была следующая функция:
getVal :: IO Int
getVal = do
putStrLn "Enter an integer value:"
n <- getLine
return $ read n
И по какой-то причине вы решили, что хотите просто раздражать пользователя и выбросить номер, который они только что передали:
getValAnnoy :: IO ()
getValAnnoy = do
_ <- getVal
return () -- Returns nothing
Однако return
является просто функцией Monad
, поэтому мы могли бы абстрагировать это немного дальше
throwAwayResult :: Monad m => m a -> m ()
throwAwayResult action = do
_ <- action
return ()
Тогда
getValAnnoy = throwAwayResult getVal
Однако вам не нужно писать эту функцию самостоятельно, она уже существует в Control.Monad
как функция void
, которая еще меньше ограничивает и работает на Functor
s:
void :: Functor f => f a -> f ()
void fa = fmap (const ()) fa
Почему он работает с Functor
вместо Monad
? Ну, для каждого экземпляра Monad
вы можете записать экземпляр Functor
как
instance Monad m => Functor m where
fmap f m = m >>= return . f
Но вы не можете сделать Monad
из каждого Functor
. Это как квадрат прямоугольника, но прямоугольник не всегда является квадратом, Монады образуют подмножество Функторов.
Ответ 2
Как говорили другие, это тип единицы, который имеет одно значение, называемое единицей. В синтаксисе Haskell это легко, если путать, выражается как () :: ()
. Мы также можем сделать свой собственный легко.
data Unit = Unit
>>> :t Unit :: Unit
Unit :: Unit
>>> :t () :: ()
() :: ()
Он записывается как ()
, потому что он очень похож на "пустой кортеж". Есть теоретические причины, почему это имеет место, но, честно говоря, это также делает очень простой интуитивный смысл.
Он часто используется в качестве аргумента для конструктора типа типа IO
или ST
, когда его контекст интересующего значения, а не самого значения. Это интуитивно верно, потому что, если я скажу вам, что у меня есть значение типа ()
, вам больше не нужно знать ничего --- там только один из них!
putStrLn :: String -> IO () -- the return type is unimportant,
-- we just want the *effect*
map (const ()) :: [a] -> [()] -- this destroys all information about a list
-- keeping only the *length*
>>> [ (), (), () ] :: [()] -- this might as well just be the number `3`
-- there is nothing else interesting about it
forward :: [()] -> Int -- we have a pair of isomorphisms even
forward = length
backward :: Int -> [()]
backward n = replicate n ()
Ответ 3
Это тип и значение.
Это тип устройства, тип, который имеет только одно значение. В Haskell его имя и только значение выглядят как пустой кортеж: ()
.
Ответ 4
Как говорили другие, ()
в Haskell - это имя типа "unit" и единственное значение указанного типа.
Одна из путаных вещей, связанных с переходом от императивного программирования к Haskell, заключается в том, что способ, которым языки имеют дело с понятием "ничего", отличается. Что более сбивает с толку словарь, потому что императивные языки и Haskell используют термин "пустота" для обозначения диаметрально разных вещей.
На императивном языке "функция" (которая не может быть истинной математической функцией) может иметь "void" в качестве возвращаемого типа, как в этом примере псевдокода:
void sayHello() {
printLn("Hello!");
}
В этом случае void
означает, что "функция", если она вернется, не приведет к результату результата. (Другая возможность заключается в том, что они не могут возвращаться, они могут зацикливаться навсегда или сбой или исключение.)
Однако в Haskell все функции (и действия IO
) должны приводить к результату. Поэтому, когда мы пишем действие IO
, которое не вызывает никакого интересного возвращаемого значения, мы возвращаем его ()
:
sayHello :: IO ()
sayHello = putStrLn "Hello!"
В последующих действиях будет просто проигнорировано значение результата ()
.
Теперь вам, вероятно, не нужно слишком беспокоиться об этом, но есть одно место, где это запутывает, а именно, что в Haskell есть тип под названием void
, но это означает нечто совершенно отличное от императивного программирования void
. Из-за этого слово "пустота" становится минным полем при сравнении Haskell и императивных языков, поскольку при переключении парадигм значение имеет значение полностью.
В Haskell void
- тип, который не имеет никаких значений. Самым большим следствием этого является то, что в Haskell функция с возвращаемым типом void
никогда не может вернуться, она может сбой только с ошибкой или циклом навсегда. Зачем? Поскольку функция возвращала бы значение типа void
, но такого значения не было.
Это, однако, не актуально, пока вы не работаете с более продвинутыми методами, поэтому вам не нужно беспокоиться об этом, кроме как остерегаться слова "пустота" .
Но большой урок состоит в том, что императив и концепции Хаскелла "нет возврата" различны. Haskell различает:
- Вещи, которые могут вернуться, но результат которых не будет иметь никакой информации (тип
()
);
- Вещи, которые не могут вернуться, независимо от того, что (тип
void
).
Императив void
соответствует первому, а не последнему.