Re: Initializing constants once, with code?
| From: | Mike Schinkel | Date: | Sun, 05 Jan 2020 21:53:24 +0000 |
| Subject: | Re: Initializing constants once, with code? | ||
| References: | 1 2 3 4 5 6 7 8 9 10 11 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-107998@lists.php.net to get a copy of this message | ||
> On Jan 5, 2020, at 3:17 PM, Rowan Tommins <rowan.collins@gmail.com> wrote:
>
> They seem a poor example of something that you would later want to "evolve" using the
> proposed new feature. An enum is, by definition, a set of values that covers all possibilities,
> often with completely arbitrary values.
Yet you called my list of URLs enums, and they are not at all arbitrary and absolutely want to
change.
We can both agree that enums for 0, 1, 2 etc. are not in use-case I am discussing.
Maybe you misspoke when you called my list of URLs enums? Or I misread?
> I'm struggling to imagine how this would work. I've seen plenty of code that needs to
> know "the current environment", but "the name of the current development
> environment" seems an odd thing to have a constant for.
I take it you have never used Pantheon hosting then? You create a named environment to match a Git
branch.
"dev" is not "local" in this content. "dev" is the place developers
push code to test remotely. Then they push to "test" for QA to evaluate. At least for
this client.
That said, my client wants to move to many "dev" and "test" environments, one
for each JIRA epic, so we would like to be able to initialize the API urls based on the current
subdomain.
> Well, it would be more "scannable" if every method in the class was written on one
> line - and indeed you can do so if you want - but that's not inherently better.
What is "better" in this context is one's opinion. You might not find it beneficial,
but I would.
> If a class has a bunch of different functions and constants in, I'll generally use an IDE
> or editor plugin to navigate them, and rely on them being well-named, rather than minimising the
> whitespace between them.
I use and have used PhpStorm extensively, since prior to 1.0. But PhpStorm does not make it easier
to do code reviews on Github, or even read code in PhpStorm. Yes, I can navigate them, but to still
adds cognitive load.
Honestly, we are debating preferences here. Can we focus on objective things?
> "Constants are defined at compile-time" is a status quo in the same sense as
> "variables can be assigned to" is a status quo - you could certainly design a language
> differently, but it's not just something that happened by accident, it's a design
> decision.
It was a design decision in that maybe it did not occur to them that it would be useful; IOW a
design by omission.
Design decisions can be changed. PHP 5 changed the design decisions of PHP objects and added the
__set() method. Before then it could have easily been argued that variable accessed a memory
location, but never called code. But then that design decision was changed.
So there is no reason I've heard thus far that this one could not be changed too.
> An interesting exercise is to ask if there are other languages that take a different decision -
> it's not decisive, either way, but it can be suggestive.
> "Can constants be evolved into something that is initialised on-demand?" is not
> something I'm aware of examples of elsewhere. That could be because nobody's thought of it
> before, or just that I haven't come across it, but it could be because there's a better
> solution to the underlying requirement.
Excellent point.
Ruby has the .replace method for constants and can thus assign at runtime:
=========
https://stackoverflow.com/a/6712438/102699
Javascript can assign constants at runtime:
=========
fn = function() { return "World"; };
const who = fn();
alert( "Hello " + who );
C++ has const member functions (not exactly the same, but similar):
=========
https://www.linuxtopia.org/online_books/programming_books/thinking_in_c++/Chapter08_014.html
Python adds a final qualifier to turn variables into constants. Thus is supports initializing
constants:
=========
https://stackoverflow.com/a/47821212/102699
http://code.activestate.com/recipes/65207-constants-in-python/
Java creates class constants with
public static final which can be initialized with
code:
=========
https://www.javaworld.com/article/2076481/use-constant-types-for-safer-and-cleaner-code.html
I think that is a fairly conclusive list of highly-used languages, all of which allow constants to
be initialized with code. This list would imply any assertion that constants must be handled at
compile time is not applicable for many other languages so I would argue to not a universal
principle.
Also it would imply that allowing constants to be initialized via runtime code would not disrupt the
time-space continuum, either.
> I think the syntax being the same when you *reference* the value bothers me more than how it is
> declared. If I see ClassName::USER_ID, I expect the value to be the same each time the code runs,
> and to be able to find the value directly in the source.
Again, I ask if that your unease has more to do with status quo bias vs. something fundamentally
wrong with the concept?
> It's a limitation in the very basic sense that it limits what you can do. You can't
> write this, for instance:
>
> class API {
> private $config;
> public function __construct($config) {
> $this->config = $config;
> }
>
> const URL {
> return $this->config->getValue('API_URL');
> }
> }
But you could do this:
class Api {
const URL:string {
return Config::get()->api_url;
}
}
class Config {
public $api_url;
private static $_current;
function __construct($args = array()){
$this->api_url = $args['api_url'];
}
static function get():Config{
if ( ! isset( self::$_current ) ) {
trigger_error('must call Config::set() before calling Config::get()');
die();
}
return self::$_current;
}
static function set( Config $config ){
self::$_current = $config;
}
}
Config::set( new Config(['api_url' => 'https://api.example.com']) );
//...
$api_url = Api::URL;
Any way, why again must perfect be the enemy of the good?
> Laravel's "facades" are global variables hidden behind static methods. So you
> write "DB::query($sql)" as a short-hand for
> "DB::$currentInstance->query($sql)". Most of the time, you just treat it as static, but
> if you want to write tests, for instance, you can inject a mock as the current instance.
Ah.
> An interesting thing is that you can evolve a class that really is static and replace it with
> one of these facades. Unfortunately, the same wouldn't be true with initialise-once constants,
> because once the constant had been read, you couldn't swap it out in another test.
Well, I am suggested one-time only because I assumed that one-time only _is_ the only thing that is
fundamental to const. But I can see uses for being able to redefine them — when
running unit tests — but I was not planning to ask for that.
> Many people would say the kind of global state you're talking about is an anti-pattern.
Well, Larry did, which is reality is an appeal-to-authority argument and not an argument on the
merits.
But I used "anti-pattern" because I did not think I was trying to convince anyone against
adding a pre-processor, so it was just easier to write!
> I'm just looking for solutions to your use case, which as I understand it is values which
> change rarely based on configuration.
I honestly do appreciate it. You are generally very good about that kind of thing.
-Mike