modularml / mojo

The Mojo Programming Language
https://docs.modular.com/mojo/manual/
Other
22.97k stars 2.59k forks source link

[Feature Request] Compare a type with other types #241

Open survuvi opened 1 year ago

survuvi commented 1 year ago

Request

Compare a type with another type

Motivation

A functionality that Python does not have without libraries like NumPy.

Description and Requirements

Could be useful, for example, to compare a single number with an array:


print(5 < [1,3,7])

# The output sholud be: [False, False, True]
Mogball commented 1 year ago

Are you suggesting that we add elementwise comparators to our list type? Our list type isn't super-functional at the moment.

DayDun commented 1 year ago

I personally prefer quite strict type safety. It feels like a feature like this added to the default list type would more often be used on accident than on purpose, which could lead to silent bugs. I definitely wouldn't mind having a more explicit type with this functionality though, such as 5 < ListPairwise(1, 3, 7) or something.

You can also do this with SIMD today already. Not that SIMD is what you want to use in general for pairwise list operations.

from DType import DType
print(SIMD[DType.ui32, 4](1, 3, 7, 5) > 5)
survuvi commented 1 year ago

@DayDun why if we extend the vector to 5 elements or we reduce it to 3 elements, we get errors?


from DType import DType
print(SIMD[DType.ui32, 5](1, 3, 7, 5, 8) > 5)

_error: Expression [15]:9:1: no viable expansions found fn __lldb_expr(inout mojo_repl_arg: mojo_repl_context): ^

Expression [15]:11:28: call expansion failed mojo_repl_expr_impl(mojo_repl_arg, __get_address_as_lvalue(mojo_repl_arg.np.load().address), get_address_as_lvalue(mojo_repl_arg.a.load().address), get_address_as_lvalue(mojo_repl_arg.result.load().address)) ^

Expression [15]:15:1: no viable expansions found def mojo_repl_expr_impl(inout mojo_repl_arg: mojo_repl_context, inout np: __mlir_type.!kgen.declref<@"$PythonObject"::@PythonObject>, inout a: mlir_type.!kgen.declref<@"$PythonObject"::@PythonObject>, inout result: __mlir_type.!kgen.declref<@"$PythonObject"::@PythonObject>) -> None: ^

Expression [15]:22:26: call expansion failed __mojo_repl_expr_body__() ^

Expression [15]:17:3: no viable expansions found def __mojo_repl_expr_body__() -> None: ^

Expression [15]:19:30: call expansion failed print(SIMD[DType.ui32, 5](1, 3, 7, 5, 8) > 5) ^

/.modular/Kernels/mojo/Stdlib/SIMD.mojo:19:1: no viable expansions found fn _simd_construction_checks[size: Int](): ^

/.modular/Kernels/mojo/Stdlib/SIMD.mojo:28:75: constraint failed: simd width must be power of 2 assert_param_msg[is_power_of2(size), "simd width must be power of 2"]()

However we can do the same using numpy:


from PythonInterface import Python

# This is equivalent to Python's `import numpy as np`
let np = Python.import_module("numpy")

# Now use numpy like Python
let a = np.array([1, 3, 7, 5])

# Compare the number to the array
result = a > 5

print(result)
DayDun commented 1 year ago

Because as I said, SIMD is not what you want to use in general for pairwise list operations. SIMD is a low level CPU optimization, and only supports lengths that are a power of two.

ekinimo commented 1 year ago

julias dot syntax is cool for such stuff its quite handy especially in conjunction with pipe (|>). Its way cleaner imho. That being said im totally fine with having inline closures and higher order functions like map , than you can simply write


print(map( lambda x: x < 5, [1,3,7])
lsh commented 1 year ago

Another interesting way to consider this request is less about type comparison and more a request for first class rank polymorphism. There is precedent in numpy for this behavior.