nri-prelude-0.1.0.0: src/Basics.hs
-- The 'ignore-exports' option allows us to define our own documentation for ==
-- and \= while secretly exporting the original Haskell definitions, which are
-- the only ones that can be used in defining custom Eq typeclasses.
-- | Tons of useful functions that get imported by default.
module Basics
( -- * Math
Int,
Float,
(+),
(-),
(*),
(/),
(//),
(^),
toFloat,
round,
floor,
ceiling,
truncate,
-- * Equality
(==),
(/=),
-- * Comparison
-- | These functions only work on @Order@-able types. This includes numbers,
-- characters, strings, lists of comparable things, and tuples of comparable
-- things.
(<),
(>),
(<=),
(>=),
max,
min,
compare,
Order,
Prelude.Ordering (Prelude.LT, Prelude.GT, Prelude.EQ),
-- * Booleans
Prelude.Bool (..),
not,
(&&),
(||),
xor,
-- * Append Strings and Lists
(++),
-- * Fancier Math
modBy,
remainderBy,
negate,
abs,
clamp,
sqrt,
logBase,
e,
-- * Angles
degrees,
radians,
turns,
-- * Trigonometry
pi,
cos,
sin,
tan,
acos,
asin,
atan,
atan2,
-- * Polar Coordinates
toPolar,
fromPolar,
-- * Floating Point Checks
isNaN,
isInfinite,
-- * Function Helpers
identity,
always,
(<|),
(|>),
(<<),
(>>),
Never,
never,
-- * Often-implemented typeclasses
Prelude.Eq,
Prelude.Ord,
Prelude.Num,
)
where
import qualified Data.Bits (xor)
import qualified Data.Int (Int64)
import qualified Data.Void (Void, absurd)
import Prelude (otherwise)
import qualified Prelude
-- INFIX OPERATORS
infixr 0 <|
infixl 0 |>
infixr 2 ||
infixr 3 &&
infix 4 ==
infix 4 /=
infix 4 <
infix 4 >
infix 4 <=
infix 4 >=
infixr 5 ++
infixl 6 +
infixl 6 -
infixl 7 *
infixl 7 /
infixl 7 //
infixr 8 ^
infixl 9 <<
infixr 9 >>
-- | An @Int@ is a whole number. Valid syntax for integers includes:
--
-- > 0
-- > 42
-- > 9000
-- > 0xFF -- 255 in hexadecimal
-- > 0x000A -- 10 in hexadecimal
--
-- __Note:__ @Int@ math is well-defined in the range @-2^31@ to @2^31 - 1@.
--
-- __Historical Note:__ The name @Int@ comes from the term [integer](https://en.wikipedia.org/wiki/Integer). It appears that the @int@ abbreviation was introduced in [ALGOL 68](https://en.wikipedia.org/wiki/ALGOL_60), shortening it from @integer@ in [ALGOL 60](https://en.wikipedia.org/wiki/ALGOL_68). Today, almost all programming languages use this abbreviation.
type Int = Data.Int.Int64
-- | A @Float@ is a [floating-point number](https://en.wikipedia.org/wiki/Floating-point_arithmetic). Valid syntax for floats includes:
--
-- > 0
-- > 42
-- > 3.14
-- > 0.1234
-- > 6.022e23 -- == (6.022 * 10^23)
-- > 6.022e+23 -- == (6.022 * 10^23)
-- > 1.602e-19 -- == (1.602 * 10^-19)
-- > 1e3 -- == (1 * 10^3) == 1000
--
-- __Historical Note:__ The particular details of floats (e.g. @NaN@) are
-- specified by [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) which is
-- literally hard-coded into almost all CPUs in the world. That means if you
-- think @NaN@ is weird, you must successfully overtake Intel and AMD with a
-- chip that is not backwards compatible with any widely-used assembly
-- language.
type Float = Prelude.Double
-- | Add two numbers. The @number@ type variable means this operation can be
-- specialized to @Int -> Int -> Int@ or to @Float -> Float -> Float@. So you
-- can do things like this:
--
-- > 3002 + 4004 == 7006 -- all ints
-- > 3.14 + 3.14 == 6.28 -- all floats
--
-- You _cannot_ add an @Int@ and a @Float@ directly though. Use functions like
-- 'toFloat' or 'round' to convert both values to the same type. So if you
-- needed to add a list length to a @Float@ for some reason, you could say one
-- of these:
--
-- > 3.14 + toFloat (List.length [1,2,3]) == 6.14
-- > round 3.14 + List.length [1,2,3] == 6
--
-- __Note:__ Languages like Java and JavaScript automatically convert @Int@
-- values to @Float@ values when you mix and match. This can make it difficult
-- to be sure exactly what type of number you are dealing with. When you try to
-- /infer/ these conversions (as Scala does) it can be even more confusing. Elm
-- has opted for a design that makes all conversions explicit.
(+) :: Prelude.Num number => number -> number -> number
(+) =
(Prelude.+)
-- | Subtract numbers like @4 - 3 == 1@.
--
-- See @'(+)'@ for docs on the @number@ type variable.
(-) :: Prelude.Num number => number -> number -> number
(-) =
(Prelude.-)
-- | Multiply numbers like @2 * 3 == 6@.
--
-- See @'(+)'@ for docs on the @number@ type variable.
(*) :: Prelude.Num number => number -> number -> number
(*) =
(Prelude.*)
-- | Floating-point division:
--
-- > 3.14 / 2 == 1.57
(/) :: Float -> Float -> Float
(/) =
(Prelude./)
-- | Integer division:
--
-- > 3 // 2 == 1
--
-- Notice that the remainder is discarded.
(//) :: Int -> Int -> Int
(//) =
-- Prelude.div truncates towards negative infinity which differs from Elm's
-- behaviour.
Prelude.quot
-- | Exponentiation
--
-- > 3^2 == 9
-- > 3^3 == 27
--
-- Breaking from Elm here, in that this is only defined for @Float@ arguments.
-- The exponentiation story in Haskell is a little more complex. See the @(^)@,
-- @(^^)@, and @(__)@ operations as a starting point.
(^) :: Float -> Float -> Float
(^) =
(Prelude.**)
-- * Int to Float / Float to Int
-- | Convert an integer into a float. Useful when mixing @Int@ and @Float@
-- values like this:
--
-- > halfOf :: Int -> Float
-- > halfOf number =
-- > toFloat number / 2
toFloat :: Int -> Float
toFloat =
Prelude.fromIntegral
-- | Round a number to the nearest integer.
--
-- > round 1.0 == 1
-- > round 1.2 == 1
-- > round 1.5 == 2
-- > round 1.8 == 2
--
-- > round -1.2 == -1
-- > round -1.5 == -1
-- > round -1.8 == -2
round :: Float -> Int
round =
Prelude.round
-- | Floor function, rounding down.
--
-- > floor 1.0 == 1
-- > floor 1.2 == 1
-- > floor 1.5 == 1
-- > floor 1.8 == 1
--
-- > floor -1.2 == -2
-- > floor -1.5 == -2
-- > floor -1.8 == -2
floor :: Float -> Int
floor =
Prelude.floor
-- | Ceiling function, rounding up.
--
-- > ceiling 1.0 == 1
-- > ceiling 1.2 == 2
-- > ceiling 1.5 == 2
-- > ceiling 1.8 == 2
--
-- > ceiling -1.2 == -1
-- > ceiling -1.5 == -1
-- > ceiling -1.8 == -1
ceiling :: Float -> Int
ceiling =
Prelude.ceiling
-- | Truncate a number, rounding towards zero.
--
-- > truncate 1.0 == 1
-- > truncate 1.2 == 1
-- > truncate 1.5 == 1
-- > truncate 1.8 == 1
--
-- > truncate -1.2 == -1
-- > truncate -1.5 == -1
-- > truncate -1.8 == -1
truncate :: Float -> Int
truncate =
Prelude.truncate
-- | Check if values are “the same”.
--
-- Breaking from Elm, this relies on Haskell's @Eq@ typeclass. For example:
--
-- > data Foo = Bar | Baz deriving (Eq)
(==) :: Prelude.Eq a => a -> a -> Bool
(==) =
(Prelude.==)
-- | Check if values are not “the same”.
--
-- Like with @(==)@, this relies on Haskell's @Eq@ typeclass.
--
-- So @(a /= b)@ is the same as @(not (a == b))@.
(/=) :: Prelude.Eq a => a -> a -> Bool
(/=) =
(Prelude./=)
-- |
(<) :: Prelude.Ord comparable => comparable -> comparable -> Bool
(<) =
(Prelude.<)
-- |
(>) :: Prelude.Ord comparable => comparable -> comparable -> Bool
(>) =
(Prelude.>)
-- |
(<=) :: Prelude.Ord comparable => comparable -> comparable -> Bool
(<=) =
(Prelude.<=)
-- |
(>=) :: Prelude.Ord comparable => comparable -> comparable -> Bool
(>=) =
(Prelude.>=)
-- | Find the larger of two comparables.
--
-- > max 42 12345678 == 12345678
-- > max "abc" "xyz" == "xyz"
max :: Prelude.Ord comparable => comparable -> comparable -> comparable
max =
Prelude.max
-- | Find the smaller of two comparables.
--
-- > min 42 12345678 == 42
-- > min "abc" "xyz" == "abc"
min :: Prelude.Ord comparable => comparable -> comparable -> comparable
min =
Prelude.min
-- | Compare any two comparable values. Comparable values include @String@,
-- @Char@, @Int@, @Float@, or a list or tuple containing comparable values. These
-- are also the only values that work as @Dict@ keys or @Set@ members.
--
-- > compare 3 4 == LT
-- > compare 4 4 == EQ
-- > compare 5 4 == GT
compare :: Prelude.Ord comparable => comparable -> comparable -> Order
compare =
Prelude.compare
-- | Represents the relative ordering of two things.
-- The relations are less than, equal to, and greater than.
type Order = Prelude.Ordering
-- | A “Boolean” value. It can either be @True@ or @False@.
--
-- __Note:__ Programmers coming from JavaScript, Java, etc. tend to reach for boolean values way too often in Elm. Using a [union type](https://guide.elm-lang.org/types/union_types.html) is often clearer and more reliable. You can learn more about this from Jeremy [here](https://youtu.be/6TDKHGtAxeg?t=1m25s) or from Richard [here](https://youtu.be/IcgmSRJHu_8?t=1m14s).
type Bool = Prelude.Bool
-- | Negate a boolean value.
--
-- > not True == False
-- > not False == True
not :: Bool -> Bool
not =
Prelude.not
-- | The logical AND operator. @True@ if both inputs are @True@.
--
-- > True && True == True
-- > True && False == False
-- > False && True == False
-- > False && False == False
(&&) :: Bool -> Bool -> Bool
(&&) =
(Prelude.&&)
-- | The logical OR operator. @True@ if one or both inputs are @True@.
--
-- > True || True == True
-- > True || False == True
-- > False || True == True
-- > False || False == False
(||) :: Bool -> Bool -> Bool
(||) =
(Prelude.||)
-- | The exclusive-or operator. @True@ if exactly one input is @True@.
--
-- > xor True True == False
-- > xor True False == True
-- > xor False True == True
-- > xor False False == False
xor :: Bool -> Bool -> Bool
xor =
Data.Bits.xor
-- | Put two appendable things together. This includes strings, lists, and text.
--
-- > "hello" ++ "world" == "helloworld"
-- > [1,1,2] ++ [3,5,8] == [1,1,2,3,5,8]
(++) :: Prelude.Semigroup appendable => appendable -> appendable -> appendable
(++) =
(Prelude.<>)
-- | Perform [modular arithmetic](https://en.wikipedia.org/wiki/Modular_arithmetic).
-- A common trick is to use (n mod 2) to detect even and odd numbers:
--
-- > modBy 2 0 == 0
-- > modBy 2 1 == 1
-- > modBy 2 2 == 0
-- > modBy 2 3 == 1
--
-- Our @modBy@ function works in the typical mathematical way when you run into
-- negative numbers:
--
-- > List.map (modBy 4) [ -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5 ]
-- > -- [ 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1 ]
--
-- Use '@remainderBy@' for a different treatment of negative numbers, or read Daan Leijen’s [Division and Modulus for Computer Scientists](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf) for more information.
modBy :: Int -> Int -> Int
modBy =
Prelude.flip Prelude.mod
-- | Get the remainder after division. Here are bunch of examples of dividing by four:
--
-- > List.map (remainderBy 4) [ -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5 ]
-- > -- [ -1, 0, -3, -2, -1, 0, 1, 2, 3, 0, 1 ]
--
-- Use '@modBy@' for a different treatment of negative numbers, or read Daan Leijen’s [Division and Modulus for Computer Scientists](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf) for more information.
remainderBy :: Int -> Int -> Int
remainderBy =
Prelude.flip Prelude.rem
-- | Negate a number.
--
-- negate 42 == -42
-- negate -42 == 42
-- negate 0 == 0
negate :: Prelude.Num number => number -> number
negate =
Prelude.negate
-- | Get the [absolute value](https://en.wikipedia.org/wiki/Absolute_value) of
-- a number.
--
-- > abs 16 == 16
-- > abs -4 == 4
-- > abs -8.5 == 8.5
-- > abs 3.14 == 3.14
abs :: Prelude.Num number => number -> number
abs =
Prelude.abs
-- | Clamps a number within a given range. With the expression
-- @clamp 100 200 x@ the results are as follows:
--
-- > 100 if x < 100
-- > x if 100 <= x < 200
-- > 200 if 200 <= x
clamp :: Prelude.Ord number => number -> number -> number -> number
clamp low high number
| number < low = low
| number > high = high
| otherwise = number
-- | Take the square root of a number.
--
-- > sqrt 4 == 2
-- > sqrt 9 == 3
-- > sqrt 16 == 4
-- > sqrt 25 == 5
sqrt :: Float -> Float
sqrt =
Prelude.sqrt
-- | Calculate the logarithm of a number with a given base.
--
-- > logBase 10 100 == 2
-- > logBase 2 256 == 8
logBase :: Float -> Float -> Float
logBase =
Prelude.logBase
-- | An approximation of e.
e :: Float
e =
Prelude.exp 1
-- | Convert radians to standard Elm angles (radians).
--
-- > radians pi == 3.141592653589793
radians :: Float -> Float
radians angleInRadians =
angleInRadians
-- | Convert degrees to standard Elm angles (radians).
--
-- > degrees 180 == 3.141592653589793
degrees :: Float -> Float
degrees angleInDegrees =
(angleInDegrees * pi) / 180
-- | Convert turns to standard Elm angles (radians). One turn is equal to 360°.
--
-- > turns (1/2) == 3.141592653589793
turns :: Float -> Float
turns angleInTurns =
(2 * pi) * angleInTurns
-- | An approximation of pi.
pi :: Float
pi =
Prelude.pi
-- | Figure out the cosine given an angle in radians.
--
-- > cos (degrees 60) == 0.5000000000000001
-- > cos (turns (1/6)) == 0.5000000000000001
-- > cos (radians (pi/3)) == 0.5000000000000001
-- > cos (pi/3) == 0.5000000000000001
cos :: Float -> Float
cos =
Prelude.cos
-- | Figure out the sine given an angle in radians.
--
-- > sin (degrees 30) == 0.49999999999999994
-- > sin (turns (1/12)) == 0.49999999999999994
-- > sin (radians (pi/6)) == 0.49999999999999994
-- > sin (pi/6) == 0.49999999999999994
sin :: Float -> Float
sin =
Prelude.sin
-- | Figure out the tangent given an angle in radians.
--
-- > tan (degrees 45) == 0.9999999999999999
-- > tan (turns (1/8)) == 0.9999999999999999
-- > tan (radians (pi/4)) == 0.9999999999999999
-- > tan (pi/4) == 0.9999999999999999
tan :: Float -> Float
tan =
Prelude.tan
-- | Figure out the arccosine for @adjacent / hypotenuse@ in radians:
--
-- > acos (1/2) == 1.0471975511965979 -- 60° or pi/3 radians
acos :: Float -> Float
acos =
Prelude.acos
-- | Figure out the arcsine for @opposite / hypotenuse@ in radians:
--
-- > asin (1/2) == 0.5235987755982989 -- 30° or pi/6 radians
asin :: Float -> Float
asin =
Prelude.asin
-- | This helps you find the angle (in radians) to an @(x,y)@ coordinate, but
-- in a way that is rarely useful in programming. __You probably want
-- 'atan2' instead!__
--
-- This version takes @y/x@ as its argument, so there is no way to know whether
-- the negative signs comes from the @y@ or @x@ value. So as we go counter-clockwise
-- around the origin from point @(1,1)@ to @(1,-1)@ to @(-1,-1)@ to @(-1,1)@ we do
-- not get angles that go in the full circle:
--
-- > atan ( 1 / 1 ) == 0.7853981633974483 -- 45° or pi/4 radians
-- > atan ( 1 / -1 ) == -0.7853981633974483 -- 315° or 7*pi/4 radians
-- > atan ( -1 / -1 ) == 0.7853981633974483 -- 45° or pi/4 radians
-- > atan ( -1 / 1 ) == -0.7853981633974483 -- 315° or 7*pi/4 radians
--
-- Notice that everything is between @pi/2@ and @-pi/2@. That is pretty useless
-- for figuring out angles in any sort of visualization, so again, check out
-- 'atan2' instead!
atan :: Float -> Float
atan =
Prelude.atan
-- | This helps you find the angle (in radians) to an @(x,y)@ coordinate.
-- So rather than saying @atan (y/x)@ you say @atan2 y x@ and you can get a full
-- range of angles:
--
-- > atan2 1 1 == 0.7853981633974483 -- 45° or pi/4 radians
-- > atan2 1 -1 == 2.356194490192345 -- 135° or 3*pi/4 radians
-- > atan2 -1 -1 == -2.356194490192345 -- 225° or 5*pi/4 radians
-- > atan2 -1 1 == -0.7853981633974483 -- 315° or 7*pi/4 radians
atan2 :: Float -> Float -> Float
atan2 =
Prelude.atan2
-- | Convert Cartesian coordinates (x,y) to polar coordinates (r,θ).
--
-- > toPolar (3, 4) == ( 5, 0.9272952180016122)
-- > toPolar (5,12) == (13, 1.1760052070951352)
toPolar :: (Float, Float) -> (Float, Float)
toPolar (x, y) =
( sqrt ((x * x) + (y * y)),
atan2 y x
)
-- | Convert polar coordinates (r,θ) to Cartesian coordinates (x,y).
--
-- > fromPolar (sqrt 2, degrees 45) == (1, 1)
fromPolar :: (Float, Float) -> (Float, Float)
fromPolar (radius, theta) =
( radius * cos theta,
radius * sin theta
)
-- | Determine whether a float is an undefined or unrepresentable number.
-- NaN stands for *not a number* and it is [a standardized part of floating point
-- numbers](https://en.wikipedia.org/wiki/NaN).
--
-- > isNaN (0/0) == True
-- > isNaN (sqrt -1) == True
-- > isNaN (1/0) == False -- infinity is a number
-- > isNaN 1 == False
isNaN :: Float -> Bool
isNaN =
Prelude.isNaN
-- | Determine whether a float is positive or negative infinity.
--
-- > isInfinite (0/0) == False
-- > isInfinite (sqrt -1) == False
-- > isInfinite (1/0) == True
-- > isInfinite 1 == False
--
-- Notice that NaN is not infinite! For float @n@ to be finite implies that
-- @not (isInfinite n || isNaN n)@ evaluates to @True@.
isInfinite :: Float -> Bool
isInfinite =
Prelude.isInfinite
-- | Given a value, returns exactly the same value. This is called
-- [the identity function](https://en.wikipedia.org/wiki/Identity_function).
identity :: a -> a
identity x =
x
-- | Create a function that *always* returns the same value. Useful with
-- functions like @map@:
--
-- > List.map (always 0) [1,2,3,4,5] == [0,0,0,0,0]
--
-- > -- List.map (\_ -> 0) [1,2,3,4,5] == [0,0,0,0,0]
-- > -- always = (\x _ -> x)
always :: a -> b -> a
always a _ =
a
-- | Saying @f <| x@ is exactly the same as @f x@.
--
-- It can help you avoid parentheses, which can be nice sometimes. Maybe you want
-- to apply a function to a @case@ expression? That sort of thing.
(<|) :: (a -> b) -> a -> b
f <| x = f x
-- | Saying @x |> f@ is exactly the same as @f x@.
--
-- It is called the “pipe” operator because it lets you write “pipelined” code.
-- For example, say we have a @sanitize@ function for turning user input into
-- integers:
--
-- > -- BEFORE
-- > sanitize :: String -> Maybe Int
-- > sanitize input =
-- > String.toInt (String.trim input)
--
-- We can rewrite it like this:
--
-- > -- AFTER
-- > sanitize :: String -> Maybe Int
-- > sanitize input =
-- > input
-- > |> String.trim
-- > |> String.toInt
--
-- Totally equivalent! I recommend trying to rewrite code that uses @x |> f@
-- into code like @f x@ until there are no pipes left. That can help you build
-- your intuition.
--
-- __Note:__ This can be overused! I think folks find it quite neat, but when you
-- have three or four steps, the code often gets clearer if you break out a
-- top-level helper function. Now the transformation has a name. The arguments are
-- named. It has a type annotation. It is much more self-documenting that way!
-- Testing the logic gets easier too. Nice side benefit!
(|>) :: a -> (a -> b) -> b
x |> f = f x
-- | Function composition, passing results along in the suggested direction. For
-- example, the following code checks if the square root of a number is odd:
--
-- > not << isEven << sqrt
--
-- You can think of this operator as equivalent to the following:
--
-- > (g << f) == (\x -> g (f x))
--
-- So our example expands out to something like this:
--
-- > \n -> not (isEven (sqrt n))
(<<) :: (b -> c) -> (a -> b) -> (a -> c)
(<<) = (Prelude..)
-- | Function composition, passing results along in the suggested direction. For
-- example, the following code checks if the square root of a number is odd:
--
-- > sqrt >> isEven >> not
(>>) :: (a -> b) -> (b -> c) -> (a -> c)
(>>) = Prelude.flip (Prelude..)
-- | A value that can never happen! For context:
--
-- - The boolean type @Bool@ has two values: @True@ and @False@
-- - The unit type @()@ has one value: @()@
-- - The never type @Never@ has no values!
--
-- You may see it in the wild in @Html Never@ which means this HTML will never
-- produce any messages. You would need to write an event handler like
-- @onClick ??? :: Attribute Never@ but how can we fill in the question marks?!
-- So there cannot be any event handlers on that HTML.
--
-- You may also see this used with tasks that never fail, like @Task Never ()@.
--
-- The @Never@ type is useful for restricting *arguments* to a function. Maybe my
-- API can only accept HTML without event handlers, so I require @Html Never@ and
-- users can give @Html msg@ and everything will go fine. Generally speaking, you
-- do not want @Never@ in your return types though.
type Never = Data.Void.Void
-- | A function that can never be called. Seems extremely pointless, but it
-- *can* come in handy. Imagine you have some HTML that should never produce any
-- messages. And say you want to use it in some other HTML that *does* produce
-- messages. You could say:
--
-- > import Html exposing (..)
-- >
-- > embedHtml :: Html Never -> Html msg
-- > embedHtml staticStuff =
-- > div []
-- > [ text "hello"
-- > , Html.map never staticStuff
-- > ]
--
-- So the @never@ function is basically telling the type system, make sure no one
-- ever calls me!
never :: Never -> a
never = Data.Void.absurd