Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:116688 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 104 invoked from network); 20 Dec 2021 22:34:05 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 20 Dec 2021 22:34:05 -0000 Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 7FDB11804E3 for ; Mon, 20 Dec 2021 15:37:38 -0800 (PST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=BAYES_20,DKIM_SIGNED, DKIM_VALID,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_NONE autolearn=no autolearn_force=no version=3.4.2 X-Spam-ASN: AS15169 209.85.128.0/17 X-Spam-Virus: No X-Envelope-From: Received: from mail-vk1-f176.google.com (mail-vk1-f176.google.com [209.85.221.176]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Mon, 20 Dec 2021 15:37:37 -0800 (PST) Received: by mail-vk1-f176.google.com with SMTP id b77so1697801vka.11 for ; Mon, 20 Dec 2021 15:37:37 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=dqxtech-net.20210112.gappssmtp.com; s=20210112; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=upCtUgxxbT3nIqzmRBR0VLZOIvb/WOncuIIF8iFN45A=; b=Dwth4EdqKeMFnvB2Bea6CVmaoSX8PgAGKb15iWataYSWC2AOcZUxNvNe2SR7eecInq 5Q5brMK1vs7xgccBu/QwzTPkeeLiYfRq8Ek02r9CQIMs39jyVypXJzcnkfi+2/t/EBp/ dd4LaUnKq/310Do+UYTDtZEcZYRb4WRs1eJ7ujocjRp6LIu9tOOkeqVmq3HOPboTrLMJ 5kK/Dkgm3Yv+16VddR++yB6aLkzX2anhEOilJjKLrfE29c/Fsa0RRHT1Eo4obyL2JxNV kO6PSTfqsSqWpzL778t7n0TYr2eE6VxtiA2Z+qvCogps6Zl7Ph2v53bMb5txb+TqpVZl oTXw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=upCtUgxxbT3nIqzmRBR0VLZOIvb/WOncuIIF8iFN45A=; b=WV2MJsHr5xMsKswDuWWTd3AWTx/glt5Vf6HNvsK4xTEupq0QXUXu8f1Or98nzDSuSD s4nqzNjgllzQC83HRHBDxGw6Rk5ILXIGPKIbO3MKN9+TRStQVvrJYxSQ1G1l5dke2jkL 5RMnAwPihUZzhe15l1sNJUNXmkdVBk1JueDMMzVpdM59ilyjnAhFXFbQpBK5gZKp5W0o MONjObf4EmGbrees28UDUl8lBbwawQTnLH+VBhlk2XEL7BGVa4a6o4s2cMHrNL0QaB5A x/84bejJEQNdTWg4vhTwnKXombLtxc1fNTcGtVzOIBfI+5JmCdR4yMuJI6RX15ENSTEo oQ3Q== X-Gm-Message-State: AOAM532uEFZSYEa5Cu+5IRFdSPw+F/P2HF3qeebkEMJxm/Gb9SXeef/M Je3j6o/HEN0XSfKmwjto31b/1DlBzLiQxw8xoayTVQ== X-Google-Smtp-Source: ABdhPJxMbFB8Odri0mzr7bOQcjKS25j7CJWEZZZi15vetzDkbUI0EH7VezL1giuxIm6DiELaBqGAiQU3DRrMcE46kZ0= X-Received: by 2002:a1f:1fc9:: with SMTP id f192mr226615vkf.33.1640043457092; Mon, 20 Dec 2021 15:37:37 -0800 (PST) MIME-Version: 1.0 References: <44b3fb4b-4693-1639-c8c0-5e17296c196e@gmail.com> <4b58c011-ed87-ba87-201d-0cf8e4116c6f@processus.org> <37dc7c09-37dc-44f5-89d5-fd3c1a256492@www.fastmail.com> In-Reply-To: <37dc7c09-37dc-44f5-89d5-fd3c1a256492@www.fastmail.com> Date: Tue, 21 Dec 2021 00:37:26 +0100 Message-ID: To: Larry Garfield Cc: php internals Content-Type: text/plain; charset="UTF-8" Subject: Re: [PHP-DEV] [RFC] User Defined Operator Overloads (v0.6) From: andreas@dqxtech.net (Andreas Hennings) On Fri, 17 Dec 2021 at 00:25, Larry Garfield wrote: > > On Thu, Dec 16, 2021, at 1:24 PM, Andreas Hennings wrote: > > > I see the distinction in overloading based on the object type on the > > left, vs overloading based on parameter types. > > > > For a method call $a->f($b), the implementation of ->f() is chosen > > based on the type of $a, but not $b. > > For an operator call "$a + $b", with the system proposed here, again, > > the implementation of "+" will be chosen based on the type of $a, but > > not $b. > > For native operator calls, the implementation is chosen based on the > > types of $a and $b, but in general they are cast to the same type > > before applying the operator. > > For global function calls f($a, $b), the implementation is always the same. > > > > In a language with parameter-based overloading, the implementation can > > be chosen based on the types of $a and $b. > > > > This brings me back to the "symmetry" concern. > > In a call "$a->f($b)", it is very clear that the implementation is owned by $a. > > However, in an operator expression "$a + $b", it looks as if both > > sides are on equal footing, whereas in reality $a "owns" the > > implementation. > > > > Add to this that due to the weak typing and implicit casting, > > developers could be completely misled by looking at an operator > > invocation, if a value (in our case just the left side) has an > > unexpected type in some edge cases. > > Especially if it is not clear whether the value is a scalar or an object. > > With a named method call, at least it is constrained to classes that > > implement a method with that name. > > The RFC covers all of this, and the way it works around it. Absent method overloading (which I don't expect any time soon, especially given how vehemently Nikita is against it), it's likely the best we could do. > > > > In a class Matrix, operator(Matrix $other): Matrix {} can be declared > > to always return Matrix, and operator(float $factor): float {} can be > > declared to always return float. > > However, with a generic operator(mixed $other): Matrix|float {}, we > > cannot natively declare when the return value will be Matrix or float. > > (a tool like psalm could still do it) > > I... have no idea what you're talking about here. The RFC as currently written is not a "generic operator". It's > > operator *(Matrix $other, bool $left): Matrix > > The implementer can type both $other and the return however they want. That could be Matrix in both cases, or it could be Matrix|float, or whatever. That's... the same as every other return type we have now. Basically the same as others have been saying in more recent comments. In a class Matrix, you might want to implement three variations of the * operator: - Matrix * Matrix = Matrix. - Matrix * float = Matrix. - Matrix * Vector = Vector. Same for other classes and operators: - Money / float = Money - Money / Money = float - Distance * Distance = Area - Distance * float = Distance Without parameter-based overloading, this needs union return types, IF we want to support all variations with operators: - Matrix * (Matrix|float|Vector) = Matrix|Vector. - Money / (Money|float) = float|Money - Distance * (Distance|float) = Area|Distance Which gives you a return type with some ambiguity. With methods, you could have different method names with dedicated return types. The naming can be awkward, so I am giving different possibilities here. - Matrix->mulFloat(float) = Matrix->scale(float) = Matrix - Matrix->mul(Matrix) = Matrix::product(Matrix, Matrix) = Matrix - Matrix->mulVector(Vector) = Vector To me, the best seems a method name that somehow predicts the return type. Possible solutions for the developer who is writing a Matrix class and who wants to use overloaded operators: - Accept the ambiguity of the return type, and use tools like psalm to be more precise. - Only use the * operator for one or 2 of the 3 variations (those that return Matrix), and introduce a regular function for the third: - Matrix * Matrix|float = Matrix - Matrix->mulVector(Vector) = Vector This "concern" is not a complete blocker for the proposal. For math-related use cases like the above, the natural expectation to use operators can be so strong that we can live with some return type ambiguity. -- Andreas > > --Larry Garfield > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php >