From 36f7f560396e7d148621c1bb160a76f1e123710b Mon Sep 17 00:00:00 2001 From: indukumar Date: Tue, 11 Jun 2024 17:32:13 +0200 Subject: [PATCH] Inferred only parameters, custom compile time checks, tuple unpacking. --- mojo/index.html | 124 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 118 insertions(+), 6 deletions(-) diff --git a/mojo/index.html b/mojo/index.html index 841d243..ee29422 100644 --- a/mojo/index.html +++ b/mojo/index.html @@ -2252,9 +2252,11 @@

Learn Mojo Programming Language

  • 13.2. Conditional execution at compile-time
  • 13.3. Parameters in functions
  • 13.4. Keyword parameters
  • -
  • 13.5. Variadic parameters
  • -
  • 13.6. Default values in parameters
  • -
  • 13.7. Parameters in structs, traits
  • +
  • 13.5. Inferred-only parameters
  • +
  • 13.6. Variadic parameters
  • +
  • 13.7. Default values in parameters
  • +
  • 13.8. Parameters in structs, traits
  • +
  • 13.9. Custom compile-time checks
  • 14. Advanced usage of functions @@ -3422,6 +3424,17 @@

    5.23. Tuple

    print("First value", access[0]) +
    +

    In def style functions, you can unpack the values of a tuple into different individual variables. The individual variables will have the right data types according to the values that are assigned. The first variable on the left-hand side gets the first value of the tuple on the right-hand side, the second variable on the left-hand side gets the second value of the tuple on the right-hand side, and so on.

    +
    +
    +
    +
        def multi_vars():
    +        a, b = (1, False)
    +        print("Variables a & b:", a, b)
    +    multi_vars()
    +
    +

    5.24. ListLiteral

    @@ -6302,7 +6315,76 @@

    13.4. Keyword parameters

    -

    13.5. Variadic parameters

    +

    13.5. Inferred-only parameters

    +
    +

    Mojo allows parameter types to depend on other parameter types. For example, suppose we have two structs, Scheme and Location. We could define Location as a struct that takes a Scheme value as its input parameter, as in Location[scheme: Scheme]. This means that Location depends on Scheme.

    +
    +
    +
    +
    struct Scheme:
    +
    +    alias HTTP = Scheme("http")
    +    alias FTP = Scheme("ftp")
    +
    +    var scheme: String
    +
    +    fn __init__(inout self, scheme: String):
    +        self.scheme = scheme
    +
    +    fn __str__(self) -> String:
    +        return self.scheme
    +
    +struct Location[scheme: Scheme]:
    +    
    +    var location: String
    +
    +    fn __init__(inout self, location: String):
    +        self.location = location
    +    
    +    fn __str__(self) -> String:
    +        return str(scheme) + "://" + self.location
    +
    +
    +
    +

    Suppose that we now define a function that uses Location. We now need to also declare Scheme parameter as otherwise the compiler does not know what the Location input parameter scheme means.

    +
    +
    +
    +
    fn print_location[scheme: Scheme, location: Location[scheme]]():
    +    print(str(location))
    +
    +
    +
    +

    This has an unfortunate impact on the ergonomics of the usage of the function, as now the caller has to specify both the Scheme and Location with again the same Scheme value. This is an unnecessary duplication.

    +
    +
    +
    +
        print_location[Scheme.FTP, Location[Scheme.FTP]("r.net")]()
    +
    +
    +
    +

    Mojo provides a solution for this. Similar to declaration of positional-only and keyword-only function arguments, Mojo provides a syntax for "inferred-only" parameters using // as the delimiter. All the parameters that are expected to be inferred will appear before the // delimiter. Those parameters are not to be passed by the caller, instead they would be automatically inferred by the compiler based on their usage in the following parameters.

    +
    +
    +
    +
    fn print_location2[scheme: Scheme, //, location: Location[scheme]]():
    +    print(str(location))
    +
    +
    +
    +

    Usage:

    +
    +
    +
    +
        print_location2[Location[Scheme.FTP]("r.net")]()
    +
    +
    +
    +

    Here we have to provide Scheme.FTP only once as the parameter scheme: Scheme will get automatically inferred.

    +
    +
    +
    +

    13.6. Variadic parameters

    Sometimes we want to be able to pass any number of parameters, without being restricted to a particular number of parameters. When we prefix a parameter with *, Mojo allows us to pass any number of values to it.

    @@ -6331,7 +6413,7 @@

    13.5. Variadic parameters

    -

    13.6. Default values in parameters

    +

    13.7. Default values in parameters

    Mojo allows default values to be used for parameters.

    @@ -6355,7 +6437,7 @@

    13.6. Default values in parameters

    -

    13.7. Parameters in structs, traits

    +

    13.8. Parameters in structs, traits

    Similar to functions, we can also pass compile-time parameters to structs, traits.

    @@ -6394,6 +6476,36 @@

    13.7. Parameters in structs, traits

    In the previous example, the parameters were passed and processed similar to how we did in functions. Basically, what we can do with parameters for functions, we can do the same for structs, and traits.

    +
    +

    13.9. Custom compile-time checks

    +
    +

    When we develop a program, we often make assumptions about the arguments we receive or the context in which we execute a function and so on. We can use if statements to validate those assumptions, but it is possible that there is a performance cost to such validations. Many programming languages provide a facility known as assertion, to validate those assumptions with minimal impact to the runtime performance of the code.

    +
    +
    +

    Mojo goes one step further by providing compile-time assertions with the function constrained.

    +
    +
    +
    +
    fn print_times[times: Int]():
    +    constrained[times > 0, "times must be greater than zero"]()
    +    for i in range(times):
    +        print(i)
    +
    +fn main():
    +    print_times[2]()
    +    print_times[0]()
    +
    +
    +
    +

    If you try to compile the code listed above, you would get a compile time error, with the message: times must be greater than zero.

    +
    +
    +

    This happens because in our function print_times we have an assertion using constrained function that checks that our compile-time parameter times is greater than zero. If we call the function with a value for times that is greater than zero, then the code compiles without any errors. However, if we pass a value that is less than or equal to zero, it will produce the same compile time error message as the one that is passed as the second parameter of the constrained function call.

    +
    +
    +

    The constrained function is quite useful to validate our assumptions about given parameters at compile-time. If during the compile-time the constrained function executes successfully, then that piece of validation code does not even have to appear in the final binary, resulting in zero performance impact at runtime.

    +
    +