T
According to your comments:List lst known during compilationIf the number of arguments does not correspond to the length of the list, a compilation error must be madeIn Haskell (as in any other language) values can only be known during performance. That's the whole point: they can be read from consoles, from the file, from the network, etc. It's impossible to know them in advance before the program is executed. During the compilation, only Types♪ For example, it is well known that the value of the file is a text line. What kind of line. valueNot known in advance, but it is known that the line (i.e.) Type)Consequently, to ensure that the list is known lst During the compilation, it is necessary that it be not relevant, namely Type♪How so? How can the list be a type? What does that mean? There's going to be some more advanced topics, so put your seatbelts on.Once upon a time, it was meant that "type" was a "data type" -- that is, "target number," "data," etc. The kinds were matched by mathematical "multiples." Literally, "type is a lot of possible variable values." But in time, it turns out there are other designs that act almost like type. Take, for example, Maybe♪ Is that a type or not? In "old" meaning, of course not: Maybe♪ Could be the type Maybe Int or Maybe Stringbut the type itself values Maybe No way. Still, Maybe Behaves a lot like a guy. For example, he could be a shabby argument.The modern Haskell allows the types to be tested in very wide ranges (from far to Idris, but still). In particular, it's possible to have a type-based list. Like this:type T = '[Int, String]
This type of list consisting of two types of elements - Int and String♪ A single quote is a special syntaxis to avoid confusing the level of values, the level of types, and the level of species (kinds).In addition, Haskell allows algebra designers to be automatically lifted, making each designer independent. For example:data F a = F -- аргумент "a" - фантомный, то есть не используется в конструкторе
x :: F Int -- инстанциирование F с параметром Int
x = F
y :: F Maybe -- инстанциирование F с параметром Maybe
y = F
z :: F True -- инстанциирование F с параметром True. Здесь конструктор True "поднят" и стал типом
z = F
This requires expansion. DataKinds♪Analogue of functions at type level family types (type families) and classes. The types of families can only be listed in other types (as normal functions can be calculated from other values), but in your case it is necessary to calculate not only the type of function but also its meaning, so the class should be used:class DefineFn (lst :: [Bool]) fn | lst -> fn where
fn :: fn
Class DefineFn has two parameters (need expansion required) MultiParamTypeClasses) One, lst - is a type of list consisting of a type of type Bool♪ Signature :: [Bool] here is the decoupling. type (kind signature). Second parameter - fn - is the type of function that can work with the list. lst♪ Signature | lst -> fn - That's it. functionally dependent (necessary expansion FunctionalDependencieswho claims that each list lst only one function fn♪It is now possible for such a class to write an implementation. Let's start on one occasion, the simplest:instance DefineFn '[False, False, False, False] (Bool -> Bool -> Bool) where
-- Возвращает False независимо от параметров, потому что все элементы списка - False
fn _ _ = False
Objection:> fn @'[False, False, False, False] True True
False
> fn @'[False, False, False, False] True True True
Ошибка: нет реализации DefineFn для списка [False, False, False, False] и функции (Bool -> Bool -> Bool -> a)
Symbol @ means "spectable type transmission." TypeApplicationsWhen I write what type I need here, not let the compiler find out on my own. Especially since he won't be able to do so here. That's the whole point: I wrote the class so I could hand over the list.Other applications can also be written:instance DefineFn '[True, False, False, False] (Bool -> Bool -> Bool) where
fn False False = True
fn _ _ = False
instance DefineFn '[True, True, False, False] (Bool -> Bool -> Bool) where
fn False False = True
fn False True = True
fn _ _ = False
Yeah. But that's how it's possible to evaporate: this reality will be 16. And for the eight-element list, it's 256!But there is a way to reduce their total number to four: instead of explicitly listing the elements of the list as True and FalseMake them parameters:instance DefineFn '[a, b, c, d] (Bool -> Bool -> Bool) where
fn x y = case (x, y) of
(False, False) -> a
(False, True) -> b
(True, False) -> c
(True, True) -> d
It's like everything in stock: depending on the parameters of the function, we're returning a part of the list. It's not compiling. Why? Because... a♪ b♪ c and d - these are types, not meanings, and they can't be returned. What do we do?But we're already able to do "from types to meaning" functions. We can use this skill:class BoolTypeToValue (b :: Bool) where
boolTypeToValue :: Bool
instance BoolTypeToValue True where
boolTypeToValue = True
instance BoolTypeToValue False where
boolTypeToValue = False
This class and its implementation make it possible to convert the type of type of type Bool in the relevant type Bool:> boolTypeToValue @True
True
> boolTypeToValue @False
False
Implementation can now be recorded. DefineFn so:instance
( BoolTypeToValue a
, BoolTypeToValue b
, BoolTypeToValue c
, BoolTypeToValue d
) => DefineFn '[a, b, c, d] (Bool -> Bool -> Bool)
where
fn x y = case (x, y) of
(False, False) -> boolTypeToValue @a
(False, True) -> boolTypeToValue @b
(True, False) -> boolTypeToValue @c
(True, True) -> boolTypeToValue @d
And similar to the eight-element list:instance
( BoolTypeToValue a
, BoolTypeToValue b
, BoolTypeToValue c
, BoolTypeToValue d
, BoolTypeToValue e
, BoolTypeToValue f
, BoolTypeToValue g
, BoolTypeToValue h
) => DefineFn '[a, b, c, d, e, f, g, h] (Bool -> Bool -> Bool -> Bool)
where
fn x y z = case (x, y, z) of
(False, False, False) -> boolTypeToValue @a
(False, False, True) -> boolTypeToValue @b
(False, True, False) -> boolTypeToValue @c
(False, True, True) -> boolTypeToValue @d
...
I also want to point out that this is all for you, Most likelyDon't. If it's just a theoretical curiosity, or maybe, well, I don't know to show which Haskell is a bad and uncomfortable language compared to, let's say Python. See my answer in that case.But if that's all you need for a practical purpose, it's from a gun on the coffins. In practice, 99 per cent of programmes do not need such complexity. If you want to do the same thing as Python, then do the same thing: pass the parameters as a list of the length of which is not tensed during the compilation, like:fn lst args
| length lst == 4 = case args of
[False, False] -> lst !! 0
[False, True] -> lst !! 1
[True, False] -> lst !! 2
[True, True] -> lst !! 3
_ -> "Неправильное количество аргументов"
...
And if you have a real practical task for which you want to use such a function, I can tell you at once 100% that your task can be made easier, safer and more effective. The main point is that the different programming languages have different " fast solutions " for the same problems, and the simplest and obvious solution from one language may not work in another. It's not gonna work well.